@lobehub/chat 0.56.0 → 0.58.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.
- package/CHANGELOG.md +71 -0
- package/package.json +2 -2
- package/src/components/SafeSpacing/index.tsx +27 -0
- package/src/const/fetch.ts +2 -0
- package/src/const/layoutTokens.ts +2 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +107 -60
- package/src/features/AvatarWithUpload/index.tsx +4 -3
- package/src/features/MobileTabBar/index.tsx +60 -0
- package/src/features/PluginSettings/PluginSettingRender.tsx +65 -0
- package/src/features/PluginSettings/index.tsx +65 -0
- package/src/features/SideBar/TopActions.tsx +1 -1
- package/src/layout/AppMobileLayout.tsx +76 -0
- package/src/locales/default/common.ts +3 -1
- package/src/locales/default/error.ts +30 -2
- package/src/locales/default/setting.ts +1 -0
- package/src/pages/api/auth.ts +2 -2
- package/src/pages/api/error.ts +3 -3
- package/src/pages/api/openai.ts +3 -3
- package/src/pages/chat/SessionList/Header.tsx +26 -2
- package/src/pages/chat/SessionList/List/Item/index.tsx +2 -2
- package/src/pages/chat/SessionList/index.tsx +10 -1
- package/src/pages/chat/[id]/index.page.tsx +19 -7
- package/src/pages/chat/[id]/mobile.tsx +31 -0
- package/src/pages/chat/[id]/setting/Header.tsx +25 -2
- package/src/pages/chat/[id]/setting/index.page.tsx +33 -16
- package/src/pages/chat/[id]/setting/mobile.tsx +11 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginError.tsx +28 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +61 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +42 -0
- package/src/pages/chat/features/Conversation/ChatList/index.tsx +2 -15
- package/src/pages/chat/features/Conversation/index.tsx +5 -5
- package/src/pages/chat/features/Header/Mobile.tsx +47 -0
- package/src/pages/chat/features/Header/PC.tsx +101 -0
- package/src/pages/chat/features/Header/index.tsx +7 -93
- package/src/pages/chat/features/Sidebar/Header.tsx +6 -4
- package/src/pages/chat/features/Sidebar/Mobile.tsx +36 -0
- package/src/pages/chat/features/Sidebar/SystemRole.tsx +2 -2
- package/src/pages/chat/features/Sidebar/index.tsx +33 -15
- package/src/pages/chat/index.page.tsx +14 -8
- package/src/pages/chat/layout.tsx +12 -8
- package/src/pages/chat/mobile.tsx +25 -0
- package/src/pages/settings/index.page.tsx +40 -8
- package/src/pages/settings/layout.tsx +5 -27
- package/src/pages/settings/mobile.tsx +20 -0
- package/src/pages/welcome/Banner/Hero.tsx +3 -5
- package/src/pages/welcome/Banner/index.tsx +5 -5
- package/src/pages/welcome/index.page.tsx +13 -4
- package/src/pages/welcome/mobile.tsx +20 -0
- package/src/pages/welcome/style.ts +2 -2
- package/src/services/plugin.ts +11 -14
- package/src/services/pluginMarket.ts +10 -0
- package/src/store/global/initialState.ts +2 -0
- package/src/store/global/selectors.ts +2 -2
- package/src/store/global/slices/common.ts +7 -0
- package/src/store/global/store.ts +7 -0
- package/src/store/plugin/action.ts +13 -2
- package/src/store/plugin/initialState.ts +3 -0
- package/src/store/plugin/selectors.ts +4 -0
- package/src/store/plugin/store.ts +2 -1
- package/src/store/session/slices/chat/actions/message.ts +11 -2
- package/src/types/fetch.ts +18 -14
- package/src/utils/fetch.ts +24 -18
- package/src/components/HeaderSpacing/index.tsx +0 -9
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
|
|
3
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
4
|
+
|
|
1
5
|
export default {
|
|
6
|
+
pluginSettings: {
|
|
7
|
+
desc: '完成以下配置,即可开始使用该插件',
|
|
8
|
+
title: '{{name}}插件配置',
|
|
9
|
+
},
|
|
2
10
|
response: {
|
|
3
11
|
400: '很抱歉,服务器不明白您的请求,请确认您的请求参数是否正确',
|
|
4
12
|
401: '很抱歉,服务器拒绝了您的请求,可能是因为您的权限不足或未提供有效的身份验证',
|
|
@@ -11,8 +19,28 @@ export default {
|
|
|
11
19
|
503: '很抱歉,服务器当前无法处理您的请求,可能是由于过载或正在进行维护,请稍后再试',
|
|
12
20
|
504: '很抱歉,服务器没有等到上游服务器的回应,请稍后再试',
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
[PluginErrorType.PluginMarketIndexNotFound]:
|
|
23
|
+
'很抱歉,服务器没有找到插件索引,请检查索引地址是否正确',
|
|
24
|
+
[PluginErrorType.PluginMarketIndexInvalid]:
|
|
25
|
+
'很抱歉,插件索引校验未通过,请检查索引文件格式是否规范',
|
|
26
|
+
[PluginErrorType.PluginMetaNotFound]:
|
|
27
|
+
'很抱歉,没有在索引中发现该插件,请插件在索引中的配置信息',
|
|
28
|
+
[PluginErrorType.PluginMetaInvalid]:
|
|
29
|
+
'很抱歉,该插件的元信息校验未通过,请检查插件元信息格式是否规范',
|
|
30
|
+
[PluginErrorType.PluginManifestNotFound]:
|
|
31
|
+
'很抱歉,服务器没有找到该插件的描述清单 (manifest.json),请检查插件描述文件地址是否正确',
|
|
32
|
+
[PluginErrorType.PluginManifestInvalid]:
|
|
33
|
+
'很抱歉,该插件的描述清单校验未通过,请检查描述清单格式是否规范',
|
|
34
|
+
[PluginErrorType.PluginApiNotFound]:
|
|
35
|
+
'很抱歉,插件描述清单中不存在该 API ,请检查你的请求方法与插件清单 API 是否匹配',
|
|
36
|
+
[PluginErrorType.PluginApiParamsError]:
|
|
37
|
+
'很抱歉,该插件请求的入参校验未通过,请检查入参与 Api 描述信息是否匹配',
|
|
38
|
+
[PluginErrorType.PluginSettingsInvalid]:
|
|
39
|
+
'很抱歉,该插件需要正确配置后才可以使用,请检查你的配置是否正确',
|
|
40
|
+
|
|
41
|
+
[ChatErrorType.InvalidAccessCode]:
|
|
42
|
+
'密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key',
|
|
43
|
+
[ChatErrorType.OpenAIBizError]: '请求 OpenAI 服务出错,请根据以下信息排查或重试',
|
|
16
44
|
},
|
|
17
45
|
unlock: {
|
|
18
46
|
apikey: {
|
package/src/pages/api/auth.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getServerConfig } from '@/config/server';
|
|
2
|
-
import {
|
|
2
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
3
3
|
|
|
4
4
|
interface AuthConfig {
|
|
5
5
|
accessCode?: string | null;
|
|
@@ -18,7 +18,7 @@ export const checkAuth = ({ apiKey, accessCode }: AuthConfig) => {
|
|
|
18
18
|
if (!ACCESS_CODE) return { auth: true };
|
|
19
19
|
|
|
20
20
|
if (accessCode !== ACCESS_CODE) {
|
|
21
|
-
return { auth: false, error:
|
|
21
|
+
return { auth: false, error: ChatErrorType.InvalidAccessCode };
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return { auth: true };
|
package/src/pages/api/error.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ErrorResponse, ErrorType } from '@/types/fetch';
|
|
1
|
+
import { ChatErrorType, ErrorResponse, ErrorType } from '@/types/fetch';
|
|
2
2
|
|
|
3
3
|
const getStatus = (errorType: ErrorType) => {
|
|
4
4
|
switch (errorType) {
|
|
5
|
-
case
|
|
5
|
+
case ChatErrorType.InvalidAccessCode: {
|
|
6
6
|
return 401;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
case
|
|
9
|
+
case ChatErrorType.OpenAIBizError: {
|
|
10
10
|
return 577;
|
|
11
11
|
}
|
|
12
12
|
}
|
package/src/pages/api/openai.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Configuration, OpenAIApi } from 'openai-edge';
|
|
|
3
3
|
|
|
4
4
|
import { getServerConfig } from '@/config/server';
|
|
5
5
|
import { createErrorResponse } from '@/pages/api/error';
|
|
6
|
-
import {
|
|
6
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
7
7
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
8
8
|
|
|
9
9
|
// 创建 OpenAI 实例
|
|
@@ -55,7 +55,7 @@ export const createChatCompletion = async ({
|
|
|
55
55
|
response = await openai.createChatCompletion(requestParams);
|
|
56
56
|
} catch (error) {
|
|
57
57
|
// 如果 await 超时报错,说明是 OpenAI 服务端的问题
|
|
58
|
-
return createErrorResponse(
|
|
58
|
+
return createErrorResponse(ChatErrorType.GatewayTimeout, { message: error });
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// ============ 4. 处理异常响应 ============ //
|
|
@@ -72,7 +72,7 @@ export const createChatCompletion = async ({
|
|
|
72
72
|
error = { message: result };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
return createErrorResponse(
|
|
75
|
+
return createErrorResponse(ChatErrorType.OpenAIBizError, { ...error, endpoint });
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// ============ 5. 发送正常相应 ============ //
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Logo, MobileNavBar, SearchBar } from '@lobehub/ui';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
3
|
import { MessageSquarePlus } from 'lucide-react';
|
|
4
4
|
import Link from 'next/link';
|
|
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
|
+
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
9
10
|
import { useSessionStore } from '@/store/session';
|
|
10
11
|
|
|
11
12
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
@@ -18,11 +19,34 @@ export const useStyles = createStyles(({ css, token }) => ({
|
|
|
18
19
|
`,
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
|
-
const Header = memo(() => {
|
|
22
|
+
const Header = memo<{ mobile?: 'navbar' | 'search' }>(({ mobile }) => {
|
|
22
23
|
const { styles } = useStyles();
|
|
23
24
|
const { t } = useTranslation('common');
|
|
24
25
|
const [keywords, createSession] = useSessionStore((s) => [s.searchKeywords, s.createSession]);
|
|
25
26
|
|
|
27
|
+
if (mobile === 'navbar') {
|
|
28
|
+
return (
|
|
29
|
+
<MobileNavBar
|
|
30
|
+
center={<Logo type={'text'} />}
|
|
31
|
+
left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
|
|
32
|
+
right={<ActionIcon icon={MessageSquarePlus} onClick={createSession} />}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (mobile === 'search')
|
|
38
|
+
return (
|
|
39
|
+
<Flexbox className={styles.top} padding={16}>
|
|
40
|
+
<SearchBar
|
|
41
|
+
allowClear
|
|
42
|
+
onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
|
|
43
|
+
placeholder={t('searchAgentPlaceholder')}
|
|
44
|
+
type={'block'}
|
|
45
|
+
value={keywords}
|
|
46
|
+
/>
|
|
47
|
+
</Flexbox>
|
|
48
|
+
);
|
|
49
|
+
|
|
26
50
|
return (
|
|
27
51
|
<Flexbox className={styles.top} gap={16} padding={16}>
|
|
28
52
|
<Flexbox distribution={'space-between'} horizontal>
|
|
@@ -6,7 +6,7 @@ import { memo, useMemo, useRef, useState } from 'react';
|
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
import { shallow } from 'zustand/shallow';
|
|
8
8
|
|
|
9
|
-
import { useGlobalStore } from '@/store/global';
|
|
9
|
+
import { settingsSelectors, useGlobalStore } from '@/store/global';
|
|
10
10
|
import { agentSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
|
11
11
|
|
|
12
12
|
import Actions from './Actions';
|
|
@@ -35,7 +35,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
|
|
|
35
35
|
const isHovering = useHover(ref);
|
|
36
36
|
|
|
37
37
|
const { styles } = useStyles();
|
|
38
|
-
const [defaultModel] = useGlobalStore((s) => [s.
|
|
38
|
+
const [defaultModel] = useGlobalStore((s) => [settingsSelectors.currentAgentConfig(s).model]);
|
|
39
39
|
|
|
40
40
|
const [
|
|
41
41
|
active,
|
|
@@ -13,7 +13,7 @@ import SessionList from './List';
|
|
|
13
13
|
|
|
14
14
|
const useStyles = createStyles(({ stylish }) => stylish.noScrollbar);
|
|
15
15
|
|
|
16
|
-
export const Sessions = memo(() => {
|
|
16
|
+
export const Sessions = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
17
17
|
const { t } = useTranslation('common');
|
|
18
18
|
const { styles } = useStyles();
|
|
19
19
|
|
|
@@ -28,6 +28,15 @@ export const Sessions = memo(() => {
|
|
|
28
28
|
[],
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
+
if (mobile)
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<Header mobile={'search'} />
|
|
35
|
+
<Inbox />
|
|
36
|
+
<CollapseGroup defaultActiveKey={['sessionList']} items={items} />
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
|
|
31
40
|
return (
|
|
32
41
|
<FolderPanel>
|
|
33
42
|
<Header />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
1
2
|
import Head from 'next/head';
|
|
2
3
|
import { memo } from 'react';
|
|
3
4
|
import { Flexbox } from 'react-layout-kit';
|
|
@@ -9,8 +10,10 @@ import Conversation from '../features/Conversation';
|
|
|
9
10
|
import Header from '../features/Header';
|
|
10
11
|
import SideBar from '../features/Sidebar';
|
|
11
12
|
import Layout from './layout';
|
|
13
|
+
import Mobile from './mobile';
|
|
12
14
|
|
|
13
15
|
const Chat = memo(() => {
|
|
16
|
+
const { mobile } = useResponsive();
|
|
14
17
|
const [avatar, title] = useSessionStore((s) => [
|
|
15
18
|
agentSelectors.currentAgentAvatar(s),
|
|
16
19
|
agentSelectors.currentAgentTitle(s),
|
|
@@ -23,13 +26,22 @@ const Chat = memo(() => {
|
|
|
23
26
|
<Head>
|
|
24
27
|
<title>{pageTitle}</title>
|
|
25
28
|
</Head>
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
{mobile ? (
|
|
30
|
+
<Mobile>
|
|
31
|
+
<Flexbox height={'calc(100vh - 44px)'} horizontal>
|
|
32
|
+
<Conversation mobile />
|
|
33
|
+
<SideBar mobile />
|
|
34
|
+
</Flexbox>
|
|
35
|
+
</Mobile>
|
|
36
|
+
) : (
|
|
37
|
+
<Layout>
|
|
38
|
+
<Header />
|
|
39
|
+
<Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
|
|
40
|
+
<Conversation />
|
|
41
|
+
<SideBar />
|
|
42
|
+
</Flexbox>
|
|
43
|
+
</Layout>
|
|
44
|
+
)}
|
|
33
45
|
</>
|
|
34
46
|
);
|
|
35
47
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useRouter } from 'next/router';
|
|
2
|
+
import { PropsWithChildren, memo } from 'react';
|
|
3
|
+
import { shallow } from 'zustand/shallow';
|
|
4
|
+
|
|
5
|
+
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
6
|
+
import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
|
|
7
|
+
|
|
8
|
+
import Header from '../features/Header';
|
|
9
|
+
|
|
10
|
+
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
+
const [activeSession, toggleTopic] = useSessionStore((s) => {
|
|
12
|
+
return [s.activeSession, s.toggleTopic];
|
|
13
|
+
}, shallow);
|
|
14
|
+
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const { id } = router.query;
|
|
17
|
+
|
|
18
|
+
useOnFinishHydrationSession(() => {
|
|
19
|
+
// 1. 正常激活会话
|
|
20
|
+
if (typeof id === 'string') {
|
|
21
|
+
activeSession(id);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 将话题重置为默认值
|
|
25
|
+
toggleTopic();
|
|
26
|
+
}, [id]);
|
|
27
|
+
|
|
28
|
+
return <AppMobileLayout navBar={<Header />}>{children}</AppMobileLayout>;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default MobileLayout;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ActionIcon,
|
|
3
|
+
ChatHeader,
|
|
4
|
+
ChatHeaderTitle,
|
|
5
|
+
MobileNavBar,
|
|
6
|
+
MobileNavBarTitle,
|
|
7
|
+
} from '@lobehub/ui';
|
|
2
8
|
import { Dropdown, MenuProps } from 'antd';
|
|
3
9
|
import { HardDriveDownload, Share2 } from 'lucide-react';
|
|
4
10
|
import Router from 'next/router';
|
|
@@ -8,7 +14,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
8
14
|
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
9
15
|
import { useSessionStore } from '@/store/session';
|
|
10
16
|
|
|
11
|
-
const Header = memo(() => {
|
|
17
|
+
const Header = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
12
18
|
const { t } = useTranslation('setting');
|
|
13
19
|
const id = useSessionStore((s) => s.activeId);
|
|
14
20
|
|
|
@@ -36,6 +42,23 @@ const Header = memo(() => {
|
|
|
36
42
|
[],
|
|
37
43
|
);
|
|
38
44
|
|
|
45
|
+
if (mobile)
|
|
46
|
+
return (
|
|
47
|
+
<MobileNavBar
|
|
48
|
+
center={<MobileNavBarTitle title={t('header.session')} />}
|
|
49
|
+
onBackClick={() => Router.back()}
|
|
50
|
+
right={
|
|
51
|
+
<>
|
|
52
|
+
<ActionIcon icon={Share2} title={t('share', { ns: 'common' })} />
|
|
53
|
+
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
|
54
|
+
<ActionIcon icon={HardDriveDownload} title={t('export', { ns: 'common' })} />
|
|
55
|
+
</Dropdown>
|
|
56
|
+
</>
|
|
57
|
+
}
|
|
58
|
+
showBackButton
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
39
62
|
return (
|
|
40
63
|
<ChatHeader
|
|
41
64
|
left={<ChatHeaderTitle title={t('header.session')} />}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
1
2
|
import isEqual from 'fast-deep-equal';
|
|
2
3
|
import Head from 'next/head';
|
|
3
4
|
import { memo } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
5
6
|
import { Flexbox } from 'react-layout-kit';
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
8
9
|
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
|
9
10
|
import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
|
|
10
11
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
@@ -12,8 +13,10 @@ import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
|
12
13
|
|
|
13
14
|
import ChatLayout from '../layout';
|
|
14
15
|
import Header from './Header';
|
|
16
|
+
import Mobile from './mobile';
|
|
15
17
|
|
|
16
18
|
const EditPage = memo(() => {
|
|
19
|
+
const { mobile } = useResponsive();
|
|
17
20
|
const { t } = useTranslation('setting');
|
|
18
21
|
const config = useSessionStore(agentSelectors.currentAgentConfig, isEqual);
|
|
19
22
|
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
|
@@ -27,26 +30,40 @@ const EditPage = memo(() => {
|
|
|
27
30
|
|
|
28
31
|
const pageTitle = genSiteHeadTitle(t('header.sessionWithName', { name: title }));
|
|
29
32
|
|
|
33
|
+
const settings = (
|
|
34
|
+
<>
|
|
35
|
+
<AgentPrompt config={config} updateConfig={updateAgentConfig} />
|
|
36
|
+
<AgentMeta
|
|
37
|
+
autocomplete={autocomplete}
|
|
38
|
+
config={config}
|
|
39
|
+
meta={meta}
|
|
40
|
+
updateMeta={updateAgentMeta}
|
|
41
|
+
/>
|
|
42
|
+
<AgentConfig config={config} updateConfig={updateAgentConfig} />
|
|
43
|
+
<AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
|
|
30
47
|
return (
|
|
31
48
|
<>
|
|
32
49
|
<Head>
|
|
33
50
|
<title>{pageTitle}</title>
|
|
34
51
|
</Head>
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</
|
|
49
|
-
|
|
52
|
+
{mobile ? (
|
|
53
|
+
<Mobile>
|
|
54
|
+
<Flexbox gap={16} padding={16}>
|
|
55
|
+
{settings}
|
|
56
|
+
</Flexbox>
|
|
57
|
+
</Mobile>
|
|
58
|
+
) : (
|
|
59
|
+
<ChatLayout>
|
|
60
|
+
<Header />
|
|
61
|
+
<Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
|
|
62
|
+
<SafeSpacing height={HEADER_HEIGHT - 16} />
|
|
63
|
+
{settings}
|
|
64
|
+
</Flexbox>
|
|
65
|
+
</ChatLayout>
|
|
66
|
+
)}
|
|
50
67
|
</>
|
|
51
68
|
);
|
|
52
69
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PropsWithChildren, memo } from 'react';
|
|
2
|
+
|
|
3
|
+
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
4
|
+
|
|
5
|
+
import Header from './Header';
|
|
6
|
+
|
|
7
|
+
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
8
|
+
return <AppMobileLayout navBar={<Header mobile />}>{children}</AppMobileLayout>;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default MobileLayout;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Highlighter } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
|
|
5
|
+
import { useStyles } from '../style';
|
|
6
|
+
|
|
7
|
+
interface OpenAIError {
|
|
8
|
+
code: 'invalid_api_key' | string;
|
|
9
|
+
message: string;
|
|
10
|
+
param?: any;
|
|
11
|
+
type: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface OpenAIErrorResponse {
|
|
15
|
+
error: OpenAIError;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const PluginError = memo<{ content: OpenAIErrorResponse; id: string }>(({ content }) => {
|
|
19
|
+
const { styles } = useStyles();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Flexbox className={styles.container} style={{ maxWidth: 600 }}>
|
|
23
|
+
<Highlighter language={'json'}>{JSON.stringify(content, null, 2)}</Highlighter>
|
|
24
|
+
</Flexbox>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export default PluginError;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Avatar } from '@lobehub/ui';
|
|
2
|
+
import { Button, Divider } from 'antd';
|
|
3
|
+
import { useTheme } from 'antd-style';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import PluginSettingsConfig from '@/features/PluginSettings';
|
|
9
|
+
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
10
|
+
import { useSessionStore } from '@/store/session';
|
|
11
|
+
|
|
12
|
+
import { ErrorActionContainer, useStyles } from '../style';
|
|
13
|
+
|
|
14
|
+
interface PluginSettingsProps {
|
|
15
|
+
id: string;
|
|
16
|
+
pluginIdentifier: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const PluginSettings = memo<PluginSettingsProps>(({ id, pluginIdentifier }) => {
|
|
20
|
+
const { styles } = useStyles();
|
|
21
|
+
|
|
22
|
+
const { t } = useTranslation('error');
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
const [resend, deleteMessage] = useSessionStore((s) => [s.resendMessage, s.deleteMessage]);
|
|
25
|
+
const plugin = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier));
|
|
26
|
+
const manifest = usePluginStore(pluginSelectors.getPluginManifestSettingsById(pluginIdentifier));
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ErrorActionContainer>
|
|
30
|
+
<Center gap={16} style={{ maxWidth: 400 }}>
|
|
31
|
+
<Avatar
|
|
32
|
+
avatar={plugin?.meta.avatar || '⚙️'}
|
|
33
|
+
background={theme.colorFillContent}
|
|
34
|
+
gap={12}
|
|
35
|
+
size={80}
|
|
36
|
+
/>
|
|
37
|
+
<Flexbox style={{ fontSize: 20 }}>
|
|
38
|
+
{t('pluginSettings.title', { name: plugin?.meta.title })}
|
|
39
|
+
</Flexbox>
|
|
40
|
+
<Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
|
|
41
|
+
<Divider style={{ margin: '0 16px' }} />
|
|
42
|
+
{manifest.settings && (
|
|
43
|
+
<PluginSettingsConfig id={manifest.identifier} settings={manifest.settings} />
|
|
44
|
+
)}
|
|
45
|
+
<Button
|
|
46
|
+
block
|
|
47
|
+
onClick={() => {
|
|
48
|
+
resend(id);
|
|
49
|
+
deleteMessage(id);
|
|
50
|
+
}}
|
|
51
|
+
style={{ marginTop: 8 }}
|
|
52
|
+
type={'primary'}
|
|
53
|
+
>
|
|
54
|
+
{t('unlock.confirm')}
|
|
55
|
+
</Button>
|
|
56
|
+
</Center>
|
|
57
|
+
</ErrorActionContainer>
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default PluginSettings;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { IPluginErrorType, PluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { RenderErrorMessage } from '@lobehub/ui';
|
|
3
|
+
|
|
4
|
+
import { ChatMessage } from '@/types/chatMessage';
|
|
5
|
+
import { ChatErrorType, ErrorType } from '@/types/fetch';
|
|
6
|
+
|
|
7
|
+
import InvalidAccess from './InvalidAccess';
|
|
8
|
+
import OpenAiBizError from './OpenAiBizError';
|
|
9
|
+
import PluginError from './Plugin/PluginError';
|
|
10
|
+
import PluginSettings from './Plugin/PluginSettings';
|
|
11
|
+
|
|
12
|
+
export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessage) => {
|
|
13
|
+
switch (error.type as IPluginErrorType) {
|
|
14
|
+
case PluginErrorType.PluginMarketIndexNotFound:
|
|
15
|
+
case PluginErrorType.PluginMarketIndexInvalid:
|
|
16
|
+
case PluginErrorType.PluginMetaInvalid:
|
|
17
|
+
case PluginErrorType.PluginMetaNotFound:
|
|
18
|
+
case PluginErrorType.PluginManifestInvalid:
|
|
19
|
+
case PluginErrorType.PluginManifestNotFound:
|
|
20
|
+
case PluginErrorType.PluginApiNotFound:
|
|
21
|
+
case PluginErrorType.PluginApiParamsError: {
|
|
22
|
+
return <PluginError content={(error as any).body} id={message.id} />;
|
|
23
|
+
}
|
|
24
|
+
case PluginErrorType.PluginSettingsInvalid: {
|
|
25
|
+
return (
|
|
26
|
+
message.plugin?.identifier && (
|
|
27
|
+
<PluginSettings id={message.id} pluginIdentifier={message.plugin?.identifier} />
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
switch (error.type as ErrorType) {
|
|
34
|
+
case ChatErrorType.InvalidAccessCode: {
|
|
35
|
+
return <InvalidAccess id={message.id} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
case ChatErrorType.OpenAIBizError: {
|
|
39
|
+
return <OpenAiBizError content={(error as any).body} id={message.id} />;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatList,
|
|
1
|
+
import { ChatList, RenderMessage } from '@lobehub/ui';
|
|
2
2
|
import isEqual from 'fast-deep-equal';
|
|
3
3
|
import { memo, useCallback } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -11,27 +11,14 @@ import {
|
|
|
11
11
|
useSessionStore,
|
|
12
12
|
} from '@/store/session';
|
|
13
13
|
import { ChatMessage } from '@/types/chatMessage';
|
|
14
|
-
import { ErrorType } from '@/types/fetch';
|
|
15
14
|
import { isFunctionMessage } from '@/utils/message';
|
|
16
15
|
|
|
17
|
-
import
|
|
18
|
-
import OpenAiBizError from './Error/OpenAiBizError';
|
|
16
|
+
import { renderErrorMessage } from './Error';
|
|
19
17
|
import MessageExtra from './MessageExtra';
|
|
20
18
|
import FunctionCall from './Plugins/FunctionCall';
|
|
21
19
|
import PluginMessage from './Plugins/PluginMessage';
|
|
22
20
|
import SkeletonList from './SkeletonList';
|
|
23
21
|
|
|
24
|
-
const renderErrorMessage: RenderErrorMessage = (error, message) => {
|
|
25
|
-
switch (error.type as ErrorType) {
|
|
26
|
-
case 'InvalidAccessCode': {
|
|
27
|
-
return <InvalidAccess id={message.id} />;
|
|
28
|
-
}
|
|
29
|
-
case 'OpenAIBizError': {
|
|
30
|
-
return <OpenAiBizError content={(error as any).body} id={message.id} />;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
22
|
const List = () => {
|
|
36
23
|
const init = useSessionChatInit();
|
|
37
24
|
const { t } = useTranslation('common');
|
|
@@ -3,19 +3,19 @@ import { memo, useRef } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
7
7
|
|
|
8
8
|
import ChatList from './ChatList';
|
|
9
9
|
import ChatInput from './Input';
|
|
10
10
|
|
|
11
|
-
const Conversation = () => {
|
|
11
|
+
const Conversation = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
12
12
|
const ref = useRef(null);
|
|
13
13
|
const { t } = useTranslation('common');
|
|
14
14
|
return (
|
|
15
15
|
<Flexbox flex={1} style={{ position: 'relative' }}>
|
|
16
16
|
<div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
|
|
17
17
|
<div ref={ref} style={{ height: '100%', overflowY: 'scroll' }}>
|
|
18
|
-
<
|
|
18
|
+
{!mobile && <SafeSpacing />}
|
|
19
19
|
<ChatList />
|
|
20
20
|
</div>
|
|
21
21
|
<BackBottom target={ref} text={t('backToBottom')} />
|
|
@@ -23,6 +23,6 @@ const Conversation = () => {
|
|
|
23
23
|
<ChatInput />
|
|
24
24
|
</Flexbox>
|
|
25
25
|
);
|
|
26
|
-
};
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
export default
|
|
28
|
+
export default Conversation;
|