@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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.58.0](https://github.com/lobehub/lobe-chat/compare/v0.57.0...v0.58.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-26**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Implement responsive design for mobile devices.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Implement responsive design for mobile devices, closes [#95](https://github.com/lobehub/lobe-chat/issues/95) ([fdb3c93](https://github.com/lobehub/lobe-chat/commit/fdb3c93))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 0.57.0](https://github.com/lobehub/lobe-chat/compare/v0.56.0...v0.57.0)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-08-26**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: Refactor to ChatErrorType.
|
|
37
|
+
|
|
38
|
+
#### ✨ Features
|
|
39
|
+
|
|
40
|
+
- **misc**: 完善插件请求的错误处理, 支持修改与记录插件的配置, 支持发送插件配置信息, 支持渲染 manifest 中的 settings, 支持设置不正确时进行插件的配置, 新增插件请求状态的错误处理.
|
|
41
|
+
|
|
42
|
+
#### 🐛 Bug Fixes
|
|
43
|
+
|
|
44
|
+
- **misc**: 修正缓存旧数据的报错问题.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### Code refactoring
|
|
52
|
+
|
|
53
|
+
- **misc**: Refactor to ChatErrorType ([cd1a033](https://github.com/lobehub/lobe-chat/commit/cd1a033))
|
|
54
|
+
|
|
55
|
+
#### What's improved
|
|
56
|
+
|
|
57
|
+
- **misc**: 完善插件请求的错误处理 ([0698d89](https://github.com/lobehub/lobe-chat/commit/0698d89))
|
|
58
|
+
- **misc**: 支持修改与记录插件的配置 ([76e8237](https://github.com/lobehub/lobe-chat/commit/76e8237))
|
|
59
|
+
- **misc**: 支持发送插件配置信息 ([2cedc85](https://github.com/lobehub/lobe-chat/commit/2cedc85))
|
|
60
|
+
- **misc**: 支持渲染 manifest 中的 settings ([1185300](https://github.com/lobehub/lobe-chat/commit/1185300))
|
|
61
|
+
- **misc**: 支持设置不正确时进行插件的配置 ([f972481](https://github.com/lobehub/lobe-chat/commit/f972481))
|
|
62
|
+
- **misc**: 新增插件请求状态的错误处理 ([228002a](https://github.com/lobehub/lobe-chat/commit/228002a))
|
|
63
|
+
|
|
64
|
+
#### What's fixed
|
|
65
|
+
|
|
66
|
+
- **misc**: 修正缓存旧数据的报错问题 ([5d8008f](https://github.com/lobehub/lobe-chat/commit/5d8008f))
|
|
67
|
+
|
|
68
|
+
</details>
|
|
69
|
+
|
|
70
|
+
<div align="right">
|
|
71
|
+
|
|
72
|
+
[](#readme-top)
|
|
73
|
+
|
|
74
|
+
</div>
|
|
75
|
+
|
|
5
76
|
## [Version 0.56.0](https://github.com/lobehub/lobe-chat/compare/v0.55.1...v0.56.0)
|
|
6
77
|
|
|
7
78
|
<sup>Released on **2023-08-24**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"lint-staged": "^14",
|
|
131
131
|
"next-pwa": "^5",
|
|
132
132
|
"node-fetch": "^3",
|
|
133
|
-
"prettier": "^
|
|
133
|
+
"prettier": "^3",
|
|
134
134
|
"remark": "^14",
|
|
135
135
|
"remark-cli": "^11",
|
|
136
136
|
"semantic-release": "^21",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MobileSafeArea, type MobileSafeAreaProps } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { HEADER_HEIGHT, MOBILE_NABBAR_HEIGHT, MOBILE_TABBAR_HEIGHT } from '@/const/layoutTokens';
|
|
5
|
+
|
|
6
|
+
interface SafeSpacingProps {
|
|
7
|
+
height?: number;
|
|
8
|
+
mobile?: boolean;
|
|
9
|
+
position?: MobileSafeAreaProps['position'];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SafeSpacing = memo<SafeSpacingProps>(({ height, position = 'top', mobile }) => {
|
|
13
|
+
let h;
|
|
14
|
+
if (mobile) {
|
|
15
|
+
h = position === 'top' ? MOBILE_NABBAR_HEIGHT : MOBILE_TABBAR_HEIGHT;
|
|
16
|
+
} else {
|
|
17
|
+
h = HEADER_HEIGHT;
|
|
18
|
+
}
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div style={{ flex: 'none', height: height || h }} />
|
|
22
|
+
{mobile && <MobileSafeArea position={position} />}
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default SafeSpacing;
|
package/src/const/fetch.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FormProps } from '@lobehub/ui';
|
|
2
2
|
|
|
3
3
|
export const HEADER_HEIGHT = 64;
|
|
4
|
+
export const MOBILE_NABBAR_HEIGHT = 44;
|
|
5
|
+
export const MOBILE_TABBAR_HEIGHT = 48;
|
|
4
6
|
export const CHAT_TEXTAREA_HEIGHT = 200;
|
|
5
7
|
export const CHAT_SIDEBAR_WIDTH = 280;
|
|
6
8
|
export const FOLDER_WIDTH = 256;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { Avatar, Form, ItemGroup } from '@lobehub/ui';
|
|
1
|
+
import { Avatar, Form, ItemGroup, Markdown } from '@lobehub/ui';
|
|
2
2
|
import { Skeleton, Switch } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
4
|
import isEqual from 'fast-deep-equal';
|
|
5
|
-
import { ToyBrick } from 'lucide-react';
|
|
6
|
-
import { memo
|
|
5
|
+
import { LucideStore, ToyBrick } from 'lucide-react';
|
|
6
|
+
import { memo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
10
|
+
import { transformPluginSettings } from '@/features/PluginSettings';
|
|
11
|
+
import PluginSettingRender from '@/features/PluginSettings/PluginSettingRender';
|
|
10
12
|
import { usePluginStore } from '@/store/plugin';
|
|
11
|
-
import {
|
|
13
|
+
import { useSessionStore } from '@/store/session';
|
|
14
|
+
import { AgentAction, agentSelectors } from '@/store/session/slices/agentConfig';
|
|
12
15
|
import { LobeAgentConfig } from '@/types/session';
|
|
13
16
|
|
|
14
|
-
const useStyles = createStyles(({ css }) => ({
|
|
17
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
15
18
|
avatar: css`
|
|
16
19
|
.ant-skeleton-header {
|
|
17
20
|
padding-right: 0;
|
|
@@ -26,6 +29,11 @@ const useStyles = createStyles(({ css }) => ({
|
|
|
26
29
|
margin-block-start: 12px !important;
|
|
27
30
|
}
|
|
28
31
|
`,
|
|
32
|
+
md: css`
|
|
33
|
+
p {
|
|
34
|
+
color: ${token.colorTextDescription};
|
|
35
|
+
}
|
|
36
|
+
`,
|
|
29
37
|
}));
|
|
30
38
|
|
|
31
39
|
export interface AgentPluginProps {
|
|
@@ -36,72 +44,111 @@ export interface AgentPluginProps {
|
|
|
36
44
|
const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
|
|
37
45
|
const { t } = useTranslation('setting');
|
|
38
46
|
const { styles } = useStyles();
|
|
39
|
-
const [useFetchPluginList, fetchPluginManifest] = usePluginStore((s) => [
|
|
47
|
+
const [useFetchPluginList, fetchPluginManifest, updatePluginSettings] = usePluginStore((s) => [
|
|
40
48
|
s.useFetchPluginList,
|
|
41
49
|
s.fetchPluginManifest,
|
|
50
|
+
s.updatePluginSettings,
|
|
42
51
|
]);
|
|
43
52
|
const pluginManifestLoading = usePluginStore((s) => s.pluginManifestLoading, isEqual);
|
|
53
|
+
const pluginManifestMap = usePluginStore((s) => s.pluginManifestMap, isEqual);
|
|
54
|
+
const pluginsSettings = usePluginStore((s) => s.pluginsSettings, isEqual);
|
|
44
55
|
const { data } = useFetchPluginList();
|
|
45
56
|
|
|
46
|
-
const
|
|
47
|
-
let children;
|
|
57
|
+
const plugins = useSessionStore(agentSelectors.currentAgentPlugins);
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
const loadingItem = {
|
|
60
|
+
avatar: (
|
|
61
|
+
<Skeleton
|
|
62
|
+
active
|
|
63
|
+
avatar={{ size: 40 }}
|
|
64
|
+
className={styles.avatar}
|
|
65
|
+
paragraph={false}
|
|
66
|
+
title={false}
|
|
67
|
+
/>
|
|
68
|
+
),
|
|
69
|
+
label: (
|
|
70
|
+
<Skeleton
|
|
71
|
+
active
|
|
72
|
+
avatar={false}
|
|
73
|
+
paragraph={{ className: styles.label, style: { marginBottom: 0 } }}
|
|
74
|
+
style={{ width: 300 }}
|
|
75
|
+
title={false}
|
|
76
|
+
/>
|
|
77
|
+
),
|
|
78
|
+
};
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const children = !data
|
|
81
|
+
? [loadingItem, loadingItem, loadingItem]
|
|
82
|
+
: data.plugins.map(({ identifier, meta }) => ({
|
|
83
|
+
avatar: <Avatar avatar={meta.avatar} />,
|
|
84
|
+
children: (
|
|
85
|
+
<Switch
|
|
86
|
+
checked={
|
|
87
|
+
// 如果在加载中,说明激活了
|
|
88
|
+
pluginManifestLoading[identifier] || !config.plugins
|
|
89
|
+
? false
|
|
90
|
+
: config.plugins.includes(identifier)
|
|
91
|
+
}
|
|
92
|
+
loading={pluginManifestLoading[identifier]}
|
|
93
|
+
onChange={(checked) => {
|
|
94
|
+
updateConfig(identifier, checked);
|
|
95
|
+
if (checked) {
|
|
96
|
+
fetchPluginManifest(identifier);
|
|
81
97
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
),
|
|
101
|
+
desc: meta?.description,
|
|
102
|
+
label: meta.title,
|
|
103
|
+
minWidth: undefined,
|
|
104
|
+
tag: identifier,
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
const pluginsConfig = !data
|
|
108
|
+
? []
|
|
109
|
+
: (plugins
|
|
110
|
+
.map((identifier) => {
|
|
111
|
+
const item = data.plugins.find((i) => i.identifier === identifier);
|
|
112
|
+
|
|
113
|
+
if (!item) return null;
|
|
114
|
+
const manifest = pluginManifestMap[identifier];
|
|
115
|
+
|
|
116
|
+
if (!manifest.settings) return null;
|
|
96
117
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
return {
|
|
119
|
+
children: transformPluginSettings(manifest.settings).map((item) => ({
|
|
120
|
+
...item,
|
|
121
|
+
children: (
|
|
122
|
+
<PluginSettingRender
|
|
123
|
+
defaultValue={pluginsSettings[identifier]?.[item.name]}
|
|
124
|
+
format={item.format}
|
|
125
|
+
onChange={(value) => {
|
|
126
|
+
updatePluginSettings(identifier, { [item.name]: value });
|
|
127
|
+
}}
|
|
128
|
+
type={item.type as any}
|
|
129
|
+
/>
|
|
130
|
+
),
|
|
131
|
+
desc: item.desc && <Markdown className={styles.md}>{item.desc}</Markdown>,
|
|
132
|
+
})),
|
|
133
|
+
icon: ToyBrick,
|
|
134
|
+
title: t('settingPlugin.config', { id: item.meta.title || item.identifier }),
|
|
135
|
+
};
|
|
136
|
+
})
|
|
137
|
+
.filter(Boolean) as unknown as ItemGroup[]);
|
|
103
138
|
|
|
104
|
-
return
|
|
139
|
+
return (
|
|
140
|
+
<Form
|
|
141
|
+
items={[
|
|
142
|
+
{
|
|
143
|
+
children,
|
|
144
|
+
icon: LucideStore,
|
|
145
|
+
title: t('settingPlugin.title'),
|
|
146
|
+
},
|
|
147
|
+
...pluginsConfig,
|
|
148
|
+
]}
|
|
149
|
+
{...FORM_STYLE}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
105
152
|
});
|
|
106
153
|
|
|
107
154
|
export default AgentPlugin;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Avatar, Logo } from '@lobehub/ui';
|
|
2
2
|
import { Upload } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { CSSProperties, memo } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
7
|
import { createUploadImageHandler } from '@/utils/uploadFIle';
|
|
@@ -25,9 +25,10 @@ const useStyle = createStyles(
|
|
|
25
25
|
interface AvatarWithUploadProps {
|
|
26
26
|
compressSize?: number;
|
|
27
27
|
size?: number;
|
|
28
|
+
style?: CSSProperties;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize = 128 }) => {
|
|
31
|
+
const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize = 128, style }) => {
|
|
31
32
|
const [avatar, setSettings] = useGlobalStore((st) => [st.settings.avatar, st.setSettings]);
|
|
32
33
|
const { styles } = useStyle();
|
|
33
34
|
|
|
@@ -67,7 +68,7 @@ const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize
|
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
return (
|
|
70
|
-
<div className={styles} style={{ maxHeight: size, maxWidth: size }}>
|
|
71
|
+
<div className={styles} style={{ maxHeight: size, maxWidth: size, ...style }}>
|
|
71
72
|
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
72
73
|
{avatar ? <Avatar avatar={avatar} size={size} /> : <Logo size={size} />}
|
|
73
74
|
</Upload>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import { MessageSquare, Settings2, Sticker } from 'lucide-react';
|
|
4
|
+
import Router from 'next/router';
|
|
5
|
+
import { rgba } from 'polished';
|
|
6
|
+
import { memo, useMemo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
|
|
11
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
12
|
+
active: css`
|
|
13
|
+
svg {
|
|
14
|
+
fill: ${rgba(token.colorPrimary, 0.25)};
|
|
15
|
+
}
|
|
16
|
+
`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
export default memo<{ className?: string }>(({ className }) => {
|
|
20
|
+
const [tab, setTab] = useGlobalStore((s) => [s.sidebarKey, s.switchSideBar]);
|
|
21
|
+
const { t } = useTranslation('common');
|
|
22
|
+
const { styles } = useStyles();
|
|
23
|
+
const items: MobileTabBarProps['items'] = useMemo(
|
|
24
|
+
() => [
|
|
25
|
+
{
|
|
26
|
+
icon: (active) => (
|
|
27
|
+
<Icon className={active ? styles.active : undefined} icon={MessageSquare} />
|
|
28
|
+
),
|
|
29
|
+
key: 'chat',
|
|
30
|
+
onClick: () => {
|
|
31
|
+
Router.push('/chat');
|
|
32
|
+
},
|
|
33
|
+
title: t('tab.chat'),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Sticker} />,
|
|
37
|
+
key: 'market',
|
|
38
|
+
|
|
39
|
+
title: t('tab.market'),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Settings2} />,
|
|
43
|
+
key: 'settings',
|
|
44
|
+
onClick: () => {
|
|
45
|
+
Router.push('/settings');
|
|
46
|
+
},
|
|
47
|
+
title: t('tab.setting'),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
[t],
|
|
51
|
+
);
|
|
52
|
+
return (
|
|
53
|
+
<MobileTabBar
|
|
54
|
+
activeKey={tab}
|
|
55
|
+
className={className}
|
|
56
|
+
items={items}
|
|
57
|
+
onChange={(key) => setTab(key as any)}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Input, InputNumber, Switch } from 'antd';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
interface PluginSettingsProps {
|
|
5
|
+
defaultValue?: any;
|
|
6
|
+
format?: string;
|
|
7
|
+
onChange: (value: any) => void;
|
|
8
|
+
props?: any;
|
|
9
|
+
type?: 'string' | 'number' | 'boolean';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const PluginSettingRender = memo<PluginSettingsProps>(
|
|
13
|
+
({ type, defaultValue, onChange, format, props }) => {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case 'string': {
|
|
16
|
+
switch (format) {
|
|
17
|
+
case 'password': {
|
|
18
|
+
return (
|
|
19
|
+
<Input.Password
|
|
20
|
+
{...props}
|
|
21
|
+
defaultValue={defaultValue}
|
|
22
|
+
onChange={(v) => {
|
|
23
|
+
onChange(v.target.value);
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return (
|
|
30
|
+
<Input
|
|
31
|
+
{...props}
|
|
32
|
+
defaultValue={defaultValue}
|
|
33
|
+
onChange={(v) => {
|
|
34
|
+
onChange(v.target.value);
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
case 'number': {
|
|
40
|
+
return (
|
|
41
|
+
<InputNumber
|
|
42
|
+
{...props}
|
|
43
|
+
defaultValue={defaultValue}
|
|
44
|
+
onChange={(v) => {
|
|
45
|
+
onChange(v);
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
case 'boolean': {
|
|
51
|
+
return (
|
|
52
|
+
<Switch
|
|
53
|
+
{...props}
|
|
54
|
+
defaultValue={defaultValue}
|
|
55
|
+
onChange={(e) => {
|
|
56
|
+
onChange(e);
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
export default PluginSettingRender;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PluginSchema } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { Form, Markdown } from '@lobehub/ui';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { usePluginStore } from '@/store/plugin';
|
|
8
|
+
|
|
9
|
+
import PluginSettingRender from './PluginSettingRender';
|
|
10
|
+
|
|
11
|
+
export const transformPluginSettings = (pluginSettings: PluginSchema) => {
|
|
12
|
+
return Object.entries(pluginSettings.properties).map(([name, i]) => ({
|
|
13
|
+
desc: i.description,
|
|
14
|
+
format: i.format,
|
|
15
|
+
label: i.title,
|
|
16
|
+
name,
|
|
17
|
+
tag: name,
|
|
18
|
+
type: i.type,
|
|
19
|
+
}));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
interface PluginSettingsConfigProps {
|
|
23
|
+
id: string;
|
|
24
|
+
settings: PluginSchema;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
28
|
+
md: css`
|
|
29
|
+
p {
|
|
30
|
+
color: ${token.colorTextDescription};
|
|
31
|
+
}
|
|
32
|
+
`,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const PluginSettingsConfig = memo<PluginSettingsConfigProps>(({ settings, id }) => {
|
|
36
|
+
const items = transformPluginSettings(settings);
|
|
37
|
+
|
|
38
|
+
const { styles } = useStyles();
|
|
39
|
+
const [updatePluginSettings] = usePluginStore((s) => [s.updatePluginSettings]);
|
|
40
|
+
const pluginSetting = usePluginStore((s) => s.pluginsSettings[id] || {}, isEqual);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Form layout={'vertical'}>
|
|
44
|
+
{items.map((item) => (
|
|
45
|
+
<Form.Item
|
|
46
|
+
desc={item.desc && <Markdown className={styles.md}>{item.desc as string}</Markdown>}
|
|
47
|
+
key={item.label}
|
|
48
|
+
label={item.label}
|
|
49
|
+
tag={item.tag}
|
|
50
|
+
>
|
|
51
|
+
<PluginSettingRender
|
|
52
|
+
defaultValue={pluginSetting[item.name]}
|
|
53
|
+
format={item.format}
|
|
54
|
+
onChange={(value) => {
|
|
55
|
+
updatePluginSettings(id, { [item.name]: value });
|
|
56
|
+
}}
|
|
57
|
+
type={item.type as any}
|
|
58
|
+
/>
|
|
59
|
+
</Form.Item>
|
|
60
|
+
))}
|
|
61
|
+
</Form>
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export default PluginSettingsConfig;
|
|
@@ -22,8 +22,8 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
|
|
|
22
22
|
onClick={() => {
|
|
23
23
|
// 如果已经在 chat 路径下了,那么就不用再跳转了
|
|
24
24
|
if (Router.asPath.startsWith('/chat')) return;
|
|
25
|
-
|
|
26
25
|
Router.push('/chat');
|
|
26
|
+
setTab('chat');
|
|
27
27
|
}}
|
|
28
28
|
size="large"
|
|
29
29
|
title={t('tab.chat')}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type MobileNavBarTitleProps } from '@lobehub/ui';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import Head from 'next/head';
|
|
4
|
+
import { CSSProperties, PropsWithChildren, ReactNode, memo } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
|
|
7
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
8
|
+
import MobileTabBar from '@/features/MobileTabBar';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css }) => ({
|
|
11
|
+
container: css`
|
|
12
|
+
position: relative;
|
|
13
|
+
|
|
14
|
+
overflow-x: hidden;
|
|
15
|
+
overflow-y: auto;
|
|
16
|
+
|
|
17
|
+
width: 100vw;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
`,
|
|
20
|
+
mobileNavBar: css`
|
|
21
|
+
position: fixed;
|
|
22
|
+
z-index: 100;
|
|
23
|
+
top: 0;
|
|
24
|
+
right: 0;
|
|
25
|
+
left: 0;
|
|
26
|
+
`,
|
|
27
|
+
mobileTabBar: css`
|
|
28
|
+
position: fixed;
|
|
29
|
+
z-index: 100;
|
|
30
|
+
right: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
left: 0;
|
|
33
|
+
`,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
interface AppMobileLayoutProps extends PropsWithChildren {
|
|
37
|
+
className?: string;
|
|
38
|
+
navBar?: ReactNode;
|
|
39
|
+
showTabBar?: boolean;
|
|
40
|
+
style?: CSSProperties;
|
|
41
|
+
title?: MobileNavBarTitleProps;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const AppMobileLayout = memo<AppMobileLayoutProps>(
|
|
45
|
+
({ children, showTabBar, navBar, style, className }) => {
|
|
46
|
+
const { styles, cx } = useStyles();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<Head>
|
|
51
|
+
<meta
|
|
52
|
+
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
|
53
|
+
name="viewport"
|
|
54
|
+
/>
|
|
55
|
+
</Head>
|
|
56
|
+
<Flexbox className={cx(styles.container, className)} style={style}>
|
|
57
|
+
{navBar && (
|
|
58
|
+
<>
|
|
59
|
+
<div className={styles.mobileNavBar}>{navBar}</div>
|
|
60
|
+
<SafeSpacing mobile position={'top'} />
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
{children}
|
|
64
|
+
{showTabBar && (
|
|
65
|
+
<>
|
|
66
|
+
<SafeSpacing mobile position={'bottom'} />
|
|
67
|
+
<MobileTabBar className={styles.mobileTabBar} />
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
</Flexbox>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export default AppMobileLayout;
|
|
@@ -63,7 +63,8 @@ export default {
|
|
|
63
63
|
stop: '停止',
|
|
64
64
|
tab: {
|
|
65
65
|
chat: '会话',
|
|
66
|
-
market: '
|
|
66
|
+
market: '发现',
|
|
67
|
+
setting: '设置',
|
|
67
68
|
},
|
|
68
69
|
temp: '临时',
|
|
69
70
|
tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
|
|
@@ -77,6 +78,7 @@ export default {
|
|
|
77
78
|
defaultTitle: '默认话题',
|
|
78
79
|
saveCurrentMessages: '将当前会话保存为话题',
|
|
79
80
|
searchPlaceholder: '搜索话题...',
|
|
81
|
+
title: '话题',
|
|
80
82
|
},
|
|
81
83
|
updateAgent: '更新助理信息',
|
|
82
84
|
updatePrompt: '更新提示词',
|