@lobehub/chat 0.26.0 → 0.27.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 (40) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/package.json +1 -1
  3. package/src/const/message.ts +2 -0
  4. package/src/locales/default/common.ts +3 -0
  5. package/src/locales/default/plugin.ts +19 -1
  6. package/src/pages/api/openai.ts +7 -4
  7. package/src/pages/api/plugins.api.ts +16 -38
  8. package/src/pages/chat/[id]/Conversation/ChatList/MessageExtra.tsx +28 -26
  9. package/src/pages/chat/[id]/Conversation/ChatList/Plugins/FunctionCall.tsx +60 -0
  10. package/src/pages/chat/[id]/Conversation/ChatList/Plugins/PluginMessage.tsx +45 -0
  11. package/src/pages/chat/[id]/Conversation/ChatList/Plugins/PluginResultRender.tsx +51 -0
  12. package/src/pages/chat/[id]/Conversation/ChatList/Plugins/style.ts +25 -0
  13. package/src/pages/chat/[id]/Conversation/ChatList/index.tsx +34 -9
  14. package/src/pages/chat/[id]/edit/AgentPlugin/index.tsx +2 -2
  15. package/src/pages/setting/index.page.tsx +11 -28
  16. package/src/pages/setting/layout.tsx +35 -0
  17. package/src/plugins/Render.ts +11 -0
  18. package/src/plugins/index.ts +7 -3
  19. package/src/plugins/searchEngine/Render.tsx +85 -0
  20. package/src/plugins/searchEngine/index.ts +4 -1
  21. package/src/plugins/searchEngine/runner.ts +7 -73
  22. package/src/plugins/searchEngine/type.ts +82 -0
  23. package/src/plugins/type.ts +17 -0
  24. package/src/plugins/weather/Render.tsx +53 -0
  25. package/src/plugins/weather/index.ts +9 -1
  26. package/src/plugins/weather/runner.ts +12 -7
  27. package/src/plugins/weather/type.ts +36 -0
  28. package/src/plugins/webCrawler/index.ts +4 -1
  29. package/src/plugins/webCrawler/runner.ts +3 -27
  30. package/src/plugins/webCrawler/type.ts +32 -0
  31. package/src/services/chatModel.ts +1 -2
  32. package/src/services/plugin.ts +30 -0
  33. package/src/store/session/slices/chat/actions/message.ts +59 -40
  34. package/src/store/session/slices/chat/selectors/utils.test.ts +3 -2
  35. package/src/store/session/slices/chat/selectors/utils.ts +4 -3
  36. package/src/types/chatMessage.ts +5 -1
  37. package/src/types/openai.ts +3 -0
  38. package/src/types/plugin.ts +4 -0
  39. package/src/utils/message.ts +16 -0
  40. package/src/pages/chat/[id]/Conversation/ChatList/FunctionMessage.tsx +0 -31
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.27.0](https://github.com/lobehub/lobe-chat/compare/v0.26.1...v0.27.0)
6
+
7
+ <sup>Released on **2023-07-29**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: 优化搜索引擎插件交互展示.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: 优化兜底结果展示.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's improved
23
+
24
+ - **misc**: 优化搜索引擎插件交互展示 ([4751084](https://github.com/lobehub/lobe-chat/commit/4751084))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: 优化兜底结果展示 ([9da45d6](https://github.com/lobehub/lobe-chat/commit/9da45d6))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
38
+ ### [Version 0.26.1](https://github.com/lobehub/lobe-chat/compare/v0.26.0...v0.26.1)
39
+
40
+ <sup>Released on **2023-07-29**</sup>
41
+
42
+ #### ♻ Code Refactoring
43
+
44
+ - **misc**: 优化 setting Layout 实现.
45
+
46
+ <br/>
47
+
48
+ <details>
49
+ <summary><kbd>Improvements and Fixes</kbd></summary>
50
+
51
+ #### Code refactoring
52
+
53
+ - **misc**: 优化 setting Layout 实现 ([f789935](https://github.com/lobehub/lobe-chat/commit/f789935))
54
+
55
+ </details>
56
+
57
+ <div align="right">
58
+
59
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
60
+
61
+ </div>
62
+
5
63
  ## [Version 0.26.0](https://github.com/lobehub/lobe-chat/compare/v0.25.0...v0.26.0)
6
64
 
7
65
  <sup>Released on **2023-07-28**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -1,2 +1,4 @@
1
1
  // 只要 start with 这个,就可以判断为 function message
2
2
  export const FUNCTION_MESSAGE_FLAG = '{"function';
3
+
4
+ export const FUNCTION_LOADING = 'FUNCTION_LOADING';
@@ -30,6 +30,9 @@ export default {
30
30
  },
31
31
  feedback: '反馈与建议',
32
32
  import: '导入配置',
33
+ message: {
34
+ function_loading: '插件请求中...',
35
+ },
33
36
  moreSetting: '更多设置...',
34
37
  newAgent: '新建助手',
35
38
  noDescription: '暂无描述',
@@ -1,10 +1,28 @@
1
1
  export default {
2
+ loading: {
3
+ content: '数据获取中...',
4
+ plugin: '插件运行中...',
5
+ },
2
6
  pluginList: '插件列表',
3
- pluginLoading: '插件运行中...',
4
7
  plugins: {
5
8
  realtimeWeather: '实时天气预报',
6
9
  searchEngine: '搜索引擎',
7
10
  undefined: '插件检测中...',
8
11
  websiteCrawler: '网页内容提取',
9
12
  },
13
+ realtimeWeather: {
14
+ data: {
15
+ date: '日期',
16
+ daytemp_float: '日间温度',
17
+ dayweather: '日间天气',
18
+ daywind: '日间风速',
19
+ nighttemp_float: '夜间温度',
20
+ nightweather: '夜间天气',
21
+ nightwind: '夜间风速',
22
+ week: '星期',
23
+ },
24
+ title: '近7日天气数据({{city}})',
25
+ updateAt: '更新时间',
26
+ },
27
+ responseData: '返回数据',
10
28
  };
@@ -4,11 +4,10 @@ import { ChatCompletionFunctions } from 'openai-edge/types/api';
4
4
 
5
5
  import { getServerConfig } from '@/config/server';
6
6
  import { createErrorResponse } from '@/pages/api/error';
7
+ import { PluginsMap } from '@/plugins';
7
8
  import { ErrorType } from '@/types/fetch';
8
9
  import { OpenAIStreamPayload } from '@/types/openai';
9
10
 
10
- import pluginList from '../../plugins';
11
-
12
11
  const isDev = process.env.NODE_ENV === 'development';
13
12
 
14
13
  // 创建 OpenAI 实例
@@ -41,7 +40,7 @@ export const createChatCompletion = async ({
41
40
 
42
41
  // ============ 1. 前置处理 functions ============ //
43
42
 
44
- const filterFunctions: ChatCompletionFunctions[] = pluginList
43
+ const filterFunctions: ChatCompletionFunctions[] = Object.values(PluginsMap)
45
44
  .filter((p) => {
46
45
  // 如果不存在 enabledPlugins,那么全部不启用
47
46
  if (!enabledPlugins) return false;
@@ -54,7 +53,11 @@ export const createChatCompletion = async ({
54
53
  const functions = filterFunctions.length === 0 ? undefined : filterFunctions;
55
54
 
56
55
  // ============ 2. 前置处理 messages ============ //
57
- const formatMessages = messages.map((m) => ({ content: m.content, role: m.role }));
56
+ const formatMessages = messages.map((m) => ({
57
+ content: m.content,
58
+ name: m.name,
59
+ role: m.role,
60
+ }));
58
61
 
59
62
  // ============ 3. 发送请求 ============ //
60
63
 
@@ -1,44 +1,22 @@
1
- import { ChatCompletionRequestMessage } from 'openai-edge';
1
+ import { OpenAIPluginPayload } from '@/types/plugin';
2
2
 
3
- import { OPENAI_API_KEY_HEADER_KEY } from '@/const/fetch';
4
- import { OpenAIStreamPayload } from '@/types/openai';
5
-
6
- import pluginList from '../../plugins';
7
- import { createChatCompletion, createOpenAI } from './openai';
3
+ import { PluginsMap } from '../../plugins';
8
4
 
9
5
  export const runtime = 'edge';
10
6
 
11
7
  export default async function handler(req: Request) {
12
- const payload = (await req.json()) as OpenAIStreamPayload;
13
- const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
14
-
15
- const openai = createOpenAI(apiKey);
16
-
17
- return await createChatCompletion({
18
- OPENAI_API_KEY: apiKey,
19
- callbacks: (payload) => ({
20
- experimental_onFunctionCall: async (
21
- { name, arguments: args },
22
- createFunctionCallMessages,
23
- ) => {
24
- console.log(`检测到 functionCall: ${name}`);
25
-
26
- const func = pluginList.find((f) => f.name === name);
27
-
28
- if (func) {
29
- const result = await func.runner(args as any);
30
-
31
- console.log(`[${name}]`, args, `result:`, JSON.stringify(result, null, 2));
32
-
33
- const newMessages = createFunctionCallMessages(result) as ChatCompletionRequestMessage[];
34
-
35
- return openai.createChatCompletion({
36
- ...payload,
37
- messages: [...payload.messages, ...newMessages],
38
- });
39
- }
40
- },
41
- }),
42
- payload,
43
- });
8
+ const { name, arguments: args } = (await req.json()) as OpenAIPluginPayload;
9
+
10
+ console.log(`检测到 functionCall: ${name}`);
11
+
12
+ const func = PluginsMap[name];
13
+
14
+ if (func) {
15
+ const data = JSON.parse(args);
16
+ const result = await func.runner(data);
17
+
18
+ console.log(`[${name}]`, args, `result:`, JSON.stringify(result, null, 2));
19
+
20
+ return new Response(JSON.stringify(result));
21
+ }
44
22
  }
@@ -1,13 +1,9 @@
1
- import { Avatar, Icon, Tooltip } from '@lobehub/ui';
2
1
  import { Tag } from 'antd';
3
2
  import { createStyles } from 'antd-style';
4
- import { LucideToyBrick } from 'lucide-react';
5
3
  import { ReactNode } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
4
  import { Flexbox } from 'react-layout-kit';
8
5
  import { shallow } from 'zustand/shallow';
9
6
 
10
- import pluginList from '@/plugins';
11
7
  import { agentSelectors, useSessionStore } from '@/store/session';
12
8
  import { ChatMessage } from '@/types/chatMessage';
13
9
 
@@ -23,25 +19,29 @@ const useStyles = createStyles(({ css }) => ({
23
19
  `,
24
20
  }));
25
21
 
26
- const MessageExtra = ({ role, extra, function_call }: ChatMessage): ReactNode => {
22
+ const MessageExtra = ({
23
+ role,
24
+ extra,
25
+ // name,
26
+ function_call,
27
+ }: ChatMessage): ReactNode => {
27
28
  const { styles } = useStyles();
28
29
 
29
- const { t } = useTranslation();
30
+ // const { t } = useTranslation();
30
31
  const [model] = useSessionStore((s) => [agentSelectors.currentAgentModel(s)], shallow);
31
32
 
32
- const plugin = pluginList.find((p) => p.name === function_call?.name);
33
- const funcTag = (
34
- <Tooltip title={function_call?.arguments}>
35
- <Tag bordered={false} className={styles.plugin} style={{ borderRadius: 6 }}>
36
- {plugin?.avatar ? (
37
- <Avatar avatar={plugin?.avatar} size={18} />
38
- ) : (
39
- <Icon icon={LucideToyBrick} />
40
- )}
41
- {t(`plugins.${function_call?.name}` as any, { ns: 'plugin' })}
42
- </Tag>
43
- </Tooltip>
44
- );
33
+ // const plugin = PluginsMap[name || ''];
34
+
35
+ // const funcTag = (
36
+ // <Tag bordered={false} className={styles.plugin} style={{ borderRadius: 6 }}>
37
+ // {plugin?.avatar ? (
38
+ // <Avatar avatar={plugin?.avatar} size={18} />
39
+ // ) : (
40
+ // <Icon icon={LucideToyBrick} />
41
+ // )}
42
+ // {t(`plugins.${name}` as any, { ns: 'plugin' })}
43
+ // </Tag>
44
+ // );
45
45
 
46
46
  const modelTag = (
47
47
  <div>
@@ -59,6 +59,7 @@ const MessageExtra = ({ role, extra, function_call }: ChatMessage): ReactNode =>
59
59
 
60
60
  switch (role) {
61
61
  case 'user':
62
+ case 'function':
62
63
  case 'system': {
63
64
  return;
64
65
  }
@@ -69,15 +70,16 @@ const MessageExtra = ({ role, extra, function_call }: ChatMessage): ReactNode =>
69
70
  if (!(hasModelTag || hasFuncTag)) return;
70
71
 
71
72
  return (
72
- <Flexbox className={styles.container} horizontal>
73
- {hasFuncTag && funcTag}
74
- {hasModelTag && modelTag}
75
- </Flexbox>
73
+ hasModelTag && (
74
+ <Flexbox className={styles.container} horizontal>
75
+ {modelTag}
76
+ </Flexbox>
77
+ )
76
78
  );
77
79
  }
78
- case 'function': {
79
- return <Flexbox className={styles.container}>{funcTag}</Flexbox>;
80
- }
80
+ // case 'function': {
81
+ // return <Flexbox className={styles.container}>{funcTag}</Flexbox>;
82
+ // }
81
83
  }
82
84
  };
83
85
 
@@ -0,0 +1,60 @@
1
+ import { LoadingOutlined } from '@ant-design/icons';
2
+ import { Avatar, Highlighter, Icon } from '@lobehub/ui';
3
+ import { LucideChevronDown, LucideChevronUp, LucideToyBrick } from 'lucide-react';
4
+ import { memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { PluginsMap } from '@/plugins';
9
+ import { OpenAIFunctionCall } from '@/types/chatMessage';
10
+
11
+ import { useStyles } from './style';
12
+
13
+ export interface FunctionCallProps {
14
+ function_call?: OpenAIFunctionCall;
15
+ loading?: boolean;
16
+ }
17
+
18
+ const FunctionCall = memo<FunctionCallProps>(({ function_call, loading }) => {
19
+ const { t } = useTranslation('plugin');
20
+ const { styles } = useStyles();
21
+ const [open, setOpen] = useState(false);
22
+
23
+ const plugin = PluginsMap[function_call?.name || ''];
24
+
25
+ const avatar = plugin?.avatar ? (
26
+ <Avatar avatar={plugin?.avatar} size={32} />
27
+ ) : (
28
+ <Icon icon={LucideToyBrick} />
29
+ );
30
+
31
+ const args = JSON.stringify(JSON.parse(function_call?.arguments || '{}'), null, 2);
32
+
33
+ return (
34
+ <Flexbox gap={8}>
35
+ <Flexbox
36
+ align={'center'}
37
+ className={styles.container}
38
+ gap={8}
39
+ horizontal
40
+ onClick={() => {
41
+ setOpen(!open);
42
+ }}
43
+ >
44
+ {loading ? (
45
+ <div>
46
+ <LoadingOutlined />
47
+ </div>
48
+ ) : (
49
+ avatar
50
+ )}
51
+ {t(`plugins.${function_call?.name}` as any, { ns: 'plugin' })}
52
+ {loading ? `(${t('loading.plugin')})` : null}
53
+ <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
54
+ </Flexbox>
55
+ {open && <Highlighter language={'json'}>{args}</Highlighter>}
56
+ </Flexbox>
57
+ );
58
+ });
59
+
60
+ export default FunctionCall;
@@ -0,0 +1,45 @@
1
+ import { Loading3QuartersOutlined } from '@ant-design/icons';
2
+ import { memo } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { PluginsRender } from '@/plugins/Render';
7
+ import { ChatMessage } from '@/types/chatMessage';
8
+
9
+ import PluginResult from './PluginResultRender';
10
+
11
+ export interface FunctionMessageProps extends ChatMessage {
12
+ loading?: boolean;
13
+ }
14
+
15
+ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
16
+ const { t } = useTranslation('plugin');
17
+
18
+ const Render = PluginsRender[name || ''];
19
+
20
+ let isJSON = true;
21
+ try {
22
+ JSON.parse(content);
23
+ } catch {
24
+ isJSON = false;
25
+ }
26
+
27
+ if (!isJSON) {
28
+ return (
29
+ <Flexbox gap={8} horizontal>
30
+ <div>
31
+ <Loading3QuartersOutlined spin />
32
+ </div>
33
+ {t('loading.content')}
34
+ </Flexbox>
35
+ );
36
+ }
37
+
38
+ if (Render) {
39
+ return <Render content={JSON.parse(content)} name={name || 'unknown'} />;
40
+ }
41
+
42
+ return <PluginResult content={content} />;
43
+ });
44
+
45
+ export default PluginMessage;
@@ -0,0 +1,51 @@
1
+ import { Avatar, Highlighter, Icon } from '@lobehub/ui';
2
+ import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
3
+ import { memo, useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useStyles } from './style';
8
+
9
+ export interface FunctionMessageProps {
10
+ content: string;
11
+ }
12
+
13
+ const PluginResult = memo<FunctionMessageProps>(({ content }) => {
14
+ const { t } = useTranslation('plugin');
15
+ const { styles } = useStyles();
16
+
17
+ const [open, setOpen] = useState(false);
18
+
19
+ let data;
20
+
21
+ try {
22
+ data = JSON.stringify(JSON.parse(content), null, 2);
23
+ } catch {
24
+ data = content;
25
+ }
26
+
27
+ return (
28
+ <Flexbox gap={8}>
29
+ <Flexbox
30
+ align={'center'}
31
+ className={styles.container}
32
+ gap={8}
33
+ horizontal
34
+ onClick={() => {
35
+ setOpen(!open);
36
+ }}
37
+ >
38
+ <Avatar avatar={'💾'} size={24} />
39
+ {t('responseData')}
40
+ <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
41
+ </Flexbox>
42
+ {open && (
43
+ <Highlighter language={'json'} style={{ maxHeight: 200, maxWidth: 600 }}>
44
+ {data}
45
+ </Highlighter>
46
+ )}
47
+ </Flexbox>
48
+ );
49
+ });
50
+
51
+ export default PluginResult;
@@ -0,0 +1,25 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const useStyles = createStyles(({ css, token }) => ({
4
+ container: css`
5
+ cursor: pointer;
6
+
7
+ width: fit-content;
8
+ padding: 6px 8px;
9
+
10
+ color: ${token.colorText};
11
+
12
+ border: 1px solid ${token.colorBorder};
13
+ border-radius: 8px;
14
+
15
+ &:hover {
16
+ background: ${token.colorFillTertiary};
17
+ }
18
+ `,
19
+ plugin: css`
20
+ display: flex;
21
+ gap: 4px;
22
+ align-items: center;
23
+ width: fit-content;
24
+ `,
25
+ }));
@@ -1,24 +1,20 @@
1
1
  import { ChatList, RenderErrorMessage, RenderMessage } from '@lobehub/ui';
2
2
  import isEqual from 'fast-deep-equal';
3
- import { memo } from 'react';
3
+ import { memo, useCallback } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
5
6
  import { shallow } from 'zustand/shallow';
6
7
 
7
8
  import { agentSelectors, chatSelectors, useSessionStore } from '@/store/session';
9
+ import { ChatMessage } from '@/types/chatMessage';
8
10
  import { ErrorType } from '@/types/fetch';
9
11
  import { isFunctionMessage } from '@/utils/message';
10
12
 
11
13
  import InvalidAccess from './Error/InvalidAccess';
12
14
  import OpenAiBizError from './Error/OpenAiBizError';
13
- import FunctionMessage from './FunctionMessage';
14
15
  import MessageExtra from './MessageExtra';
15
-
16
- const renderMessage: RenderMessage = (content, message) => {
17
- if (message.role === 'function')
18
- return isFunctionMessage(message.content) ? <FunctionMessage /> : content;
19
-
20
- return content;
21
- };
16
+ import FunctionCall from './Plugins/FunctionCall';
17
+ import PluginMessage from './Plugins/PluginMessage';
22
18
 
23
19
  const renderErrorMessage: RenderErrorMessage = (error, message) => {
24
20
  switch (error.type as ErrorType) {
@@ -46,6 +42,35 @@ const List = () => {
46
42
  shallow,
47
43
  );
48
44
 
45
+ const renderMessage: RenderMessage = useCallback(
46
+ (content, message: ChatMessage) => {
47
+ if (message.role === 'function')
48
+ return (
49
+ <Flexbox gap={12}>
50
+ <FunctionCall
51
+ function_call={message.function_call}
52
+ loading={message.id === chatLoadingId}
53
+ />
54
+ <PluginMessage loading={message.id === chatLoadingId} {...message} />
55
+ </Flexbox>
56
+ );
57
+
58
+ if (message.role === 'assistant') {
59
+ return isFunctionMessage(message.content) || !!message.function_call ? (
60
+ <FunctionCall
61
+ function_call={message.function_call}
62
+ loading={message.id === chatLoadingId}
63
+ />
64
+ ) : (
65
+ content
66
+ );
67
+ }
68
+
69
+ return content;
70
+ },
71
+ [chatLoadingId],
72
+ );
73
+
49
74
  return (
50
75
  <ChatList
51
76
  data={data}
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
7
7
  import { shallow } from 'zustand/shallow';
8
8
 
9
9
  import { FORM_STYLE } from '@/const/layoutTokens';
10
- import pluginList from '@/plugins';
10
+ import { PluginsMap } from '@/plugins';
11
11
  import { agentSelectors, useSessionStore } from '@/store/session';
12
12
 
13
13
  const PluginList = () => {
@@ -19,7 +19,7 @@ const PluginList = () => {
19
19
 
20
20
  const plugin: ItemGroup = useMemo(
21
21
  () => ({
22
- children: pluginList.map((item) => ({
22
+ children: Object.values(PluginsMap).map((item) => ({
23
23
  avatar: <Avatar avatar={item.avatar} />,
24
24
  children: (
25
25
  <Switch
@@ -1,39 +1,22 @@
1
- import Head from 'next/head';
2
- import { memo, useEffect } from 'react';
3
- import { useTranslation } from 'react-i18next';
1
+ import { memo } from 'react';
4
2
  import { Flexbox } from 'react-layout-kit';
5
3
 
6
4
  import HeaderSpacing from '@/components/HeaderSpacing';
7
- import { createI18nNext } from '@/locales/create';
8
- import ChatLayout from '@/pages/chat/layout';
9
- import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
10
5
 
11
6
  import Header from './Header';
12
7
  import SettingForm from './SettingForm';
8
+ import SettingLayout from './layout';
13
9
 
14
- const initI18n = createI18nNext('setting');
15
-
16
- const SettingLayout = memo(() => {
17
- const { t } = useTranslation('setting');
18
- const pageTitle = genSiteHeadTitle(t('header.global'));
19
-
20
- useEffect(() => {
21
- initI18n.finally();
22
- }, []);
10
+ const Setting = memo(() => {
23
11
  return (
24
- <>
25
- <Head>
26
- <title>{pageTitle}</title>
27
- </Head>
28
- <ChatLayout>
29
- <Header />
30
- <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
31
- <HeaderSpacing />
32
- <SettingForm />
33
- </Flexbox>
34
- </ChatLayout>
35
- </>
12
+ <SettingLayout>
13
+ <Header />
14
+ <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
15
+ <HeaderSpacing />
16
+ <SettingForm />
17
+ </Flexbox>
18
+ </SettingLayout>
36
19
  );
37
20
  });
38
21
 
39
- export default SettingLayout;
22
+ export default Setting;
@@ -0,0 +1,35 @@
1
+ import Head from 'next/head';
2
+ import { ReactNode, memo, useEffect } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import SideBar from '@/features/SideBar';
7
+ import { createI18nNext } from '@/locales/create';
8
+ import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
9
+
10
+ const initI18n = createI18nNext('setting');
11
+
12
+ const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
13
+ const { t } = useTranslation('setting');
14
+ const pageTitle = genSiteHeadTitle(t('header.global'));
15
+
16
+ useEffect(() => {
17
+ initI18n.finally();
18
+ }, []);
19
+
20
+ return (
21
+ <>
22
+ <Head>
23
+ <title>{pageTitle}</title>
24
+ </Head>
25
+ <Flexbox horizontal width={'100%'}>
26
+ <SideBar />
27
+ <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
28
+ {children}
29
+ </Flexbox>
30
+ </Flexbox>
31
+ </>
32
+ );
33
+ });
34
+
35
+ export default SettingLayout;
@@ -0,0 +1,11 @@
1
+ import { PluginRender } from '@/plugins/type';
2
+
3
+ import searchEngine from './searchEngine';
4
+ import SearchEngineRender from './searchEngine/Render';
5
+ import getWeather from './weather';
6
+ import WeatherRender from './weather/Render';
7
+
8
+ export const PluginsRender: Record<string, PluginRender> = {
9
+ [getWeather.name]: WeatherRender,
10
+ [searchEngine.name]: SearchEngineRender,
11
+ };