@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/package.json +2 -2
  3. package/src/components/SafeSpacing/index.tsx +27 -0
  4. package/src/const/fetch.ts +2 -0
  5. package/src/const/layoutTokens.ts +2 -0
  6. package/src/features/AgentSetting/AgentPlugin/index.tsx +107 -60
  7. package/src/features/AvatarWithUpload/index.tsx +4 -3
  8. package/src/features/MobileTabBar/index.tsx +60 -0
  9. package/src/features/PluginSettings/PluginSettingRender.tsx +65 -0
  10. package/src/features/PluginSettings/index.tsx +65 -0
  11. package/src/features/SideBar/TopActions.tsx +1 -1
  12. package/src/layout/AppMobileLayout.tsx +76 -0
  13. package/src/locales/default/common.ts +3 -1
  14. package/src/locales/default/error.ts +30 -2
  15. package/src/locales/default/setting.ts +1 -0
  16. package/src/pages/api/auth.ts +2 -2
  17. package/src/pages/api/error.ts +3 -3
  18. package/src/pages/api/openai.ts +3 -3
  19. package/src/pages/chat/SessionList/Header.tsx +26 -2
  20. package/src/pages/chat/SessionList/List/Item/index.tsx +2 -2
  21. package/src/pages/chat/SessionList/index.tsx +10 -1
  22. package/src/pages/chat/[id]/index.page.tsx +19 -7
  23. package/src/pages/chat/[id]/mobile.tsx +31 -0
  24. package/src/pages/chat/[id]/setting/Header.tsx +25 -2
  25. package/src/pages/chat/[id]/setting/index.page.tsx +33 -16
  26. package/src/pages/chat/[id]/setting/mobile.tsx +11 -0
  27. package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginError.tsx +28 -0
  28. package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +61 -0
  29. package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +42 -0
  30. package/src/pages/chat/features/Conversation/ChatList/index.tsx +2 -15
  31. package/src/pages/chat/features/Conversation/index.tsx +5 -5
  32. package/src/pages/chat/features/Header/Mobile.tsx +47 -0
  33. package/src/pages/chat/features/Header/PC.tsx +101 -0
  34. package/src/pages/chat/features/Header/index.tsx +7 -93
  35. package/src/pages/chat/features/Sidebar/Header.tsx +6 -4
  36. package/src/pages/chat/features/Sidebar/Mobile.tsx +36 -0
  37. package/src/pages/chat/features/Sidebar/SystemRole.tsx +2 -2
  38. package/src/pages/chat/features/Sidebar/index.tsx +33 -15
  39. package/src/pages/chat/index.page.tsx +14 -8
  40. package/src/pages/chat/layout.tsx +12 -8
  41. package/src/pages/chat/mobile.tsx +25 -0
  42. package/src/pages/settings/index.page.tsx +40 -8
  43. package/src/pages/settings/layout.tsx +5 -27
  44. package/src/pages/settings/mobile.tsx +20 -0
  45. package/src/pages/welcome/Banner/Hero.tsx +3 -5
  46. package/src/pages/welcome/Banner/index.tsx +5 -5
  47. package/src/pages/welcome/index.page.tsx +13 -4
  48. package/src/pages/welcome/mobile.tsx +20 -0
  49. package/src/pages/welcome/style.ts +2 -2
  50. package/src/services/plugin.ts +11 -14
  51. package/src/services/pluginMarket.ts +10 -0
  52. package/src/store/global/initialState.ts +2 -0
  53. package/src/store/global/selectors.ts +2 -2
  54. package/src/store/global/slices/common.ts +7 -0
  55. package/src/store/global/store.ts +7 -0
  56. package/src/store/plugin/action.ts +13 -2
  57. package/src/store/plugin/initialState.ts +3 -0
  58. package/src/store/plugin/selectors.ts +4 -0
  59. package/src/store/plugin/store.ts +2 -1
  60. package/src/store/session/slices/chat/actions/message.ts +11 -2
  61. package/src/types/fetch.ts +18 -14
  62. package/src/utils/fetch.ts +24 -18
  63. 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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.56.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": "^2",
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;
@@ -3,3 +3,5 @@ export const OPENAI_END_POINT = 'X-OPENAI-END_POINT';
3
3
  export const OPENAI_API_KEY_HEADER_KEY = 'X-OPENAI-API-KEY';
4
4
 
5
5
  export const LOBE_CHAT_ACCESS_CODE = 'X-LOBE_CHAT_ACCESS_CODE';
6
+
7
+ export const LOBE_PLUGIN_SETTINGS = 'X-LOBE_PLUGIN_SETTINGS';
@@ -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, useMemo } from 'react';
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 { AgentAction } from '@/store/session/slices/agentConfig';
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 plugin: ItemGroup = useMemo(() => {
47
- let children;
57
+ const plugins = useSessionStore(agentSelectors.currentAgentPlugins);
48
58
 
49
- const loadingItem = {
50
- avatar: (
51
- <Skeleton
52
- active
53
- avatar={{ size: 40 }}
54
- className={styles.avatar}
55
- paragraph={false}
56
- title={false}
57
- />
58
- ),
59
- label: (
60
- <Skeleton
61
- active
62
- avatar={false}
63
- paragraph={{ className: styles.label, style: { marginBottom: 0 } }}
64
- style={{ width: 300 }}
65
- title={false}
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
- children = !data
71
- ? [loadingItem, loadingItem, loadingItem]
72
- : data.plugins.map((item) => ({
73
- avatar: <Avatar avatar={item.meta.avatar} />,
74
- children: (
75
- <Switch
76
- checked={
77
- // 如果在加载中,说明激活了
78
- pluginManifestLoading[item.identifier] || !config.plugins
79
- ? false
80
- : config.plugins.includes(item.identifier)
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
- loading={pluginManifestLoading[item.identifier]}
83
- onChange={(checked) => {
84
- updateConfig(item.identifier, checked);
85
- if (checked) {
86
- fetchPluginManifest(item.identifier);
87
- }
88
- }}
89
- />
90
- ),
91
- desc: item.meta?.description,
92
- label: t(`plugins.${item.identifier}` as any, { ns: 'plugin' }),
93
- minWidth: undefined,
94
- tag: item.identifier,
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
- return {
98
- children,
99
- icon: ToyBrick,
100
- title: t('settingPlugin.title'),
101
- };
102
- }, [config, data?.plugins, pluginManifestLoading]);
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 <Form items={[plugin]} {...FORM_STYLE} />;
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: '更新提示词',