@lobehub/chat 1.52.13 → 1.52.14
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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/app/[variants]/{@modal/chat/(.)settings/modal/features/CategoryContent.tsx → (main)/chat/(workspace)/features/AgentSettings/CategoryContent/index.tsx} +7 -6
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +114 -0
- package/src/app/[variants]/(main)/chat/(workspace)/features/SettingButton.tsx +14 -6
- package/src/hooks/useInterceptingRoutes.test.ts +10 -5
- package/src/hooks/useInterceptingRoutes.ts +10 -8
- package/src/store/agent/slices/chat/initialState.ts +2 -0
- package/src/app/[variants]/(main)/chat/settings/modal/page.tsx +0 -23
- package/src/app/[variants]/@modal/chat/(.)settings/modal/layout.tsx +0 -61
- package/src/app/[variants]/@modal/chat/(.)settings/modal/loading.tsx +0 -5
- package/src/app/[variants]/@modal/chat/(.)settings/modal/page.tsx +0 -56
- package/src/hooks/useChatSettingsTab.ts +0 -12
- /package/src/app/[variants]/{@modal/chat/(.)settings/modal/features → (main)/chat/(workspace)/features/AgentSettings/CategoryContent}/useCategory.tsx +0 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.52.14](https://github.com/lobehub/lobe-chat/compare/v1.52.13...v1.52.14)
|
6
|
+
|
7
|
+
<sup>Released on **2025-02-10**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Refactor agent settings modal.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Refactor agent settings modal, closes [#5987](https://github.com/lobehub/lobe-chat/issues/5987) ([6482f8a](https://github.com/lobehub/lobe-chat/commit/6482f8a))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.52.13](https://github.com/lobehub/lobe-chat/compare/v1.52.12...v1.52.13)
|
6
31
|
|
7
32
|
<sup>Released on **2025-02-10**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.52.
|
3
|
+
"version": "1.52.14",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -5,22 +5,23 @@ import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
6
6
|
import HeaderContent from '@/app/[variants]/(main)/chat/settings/features/HeaderContent';
|
7
7
|
import Menu from '@/components/Menu';
|
8
|
-
import {
|
9
|
-
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
8
|
+
import { ChatSettingsTabs } from '@/store/global/initialState';
|
10
9
|
|
11
10
|
import { useCategory } from './useCategory';
|
12
11
|
|
13
|
-
|
12
|
+
interface CategoryContentProps {
|
13
|
+
setTab: (tab: ChatSettingsTabs) => void;
|
14
|
+
tab: string;
|
15
|
+
}
|
16
|
+
const CategoryContent = memo<CategoryContentProps>(({ setTab, tab }) => {
|
14
17
|
const cateItems = useCategory();
|
15
|
-
const tab = useChatSettingsTab();
|
16
|
-
const router = useQueryRoute();
|
17
18
|
|
18
19
|
return (
|
19
20
|
<>
|
20
21
|
<Menu
|
21
22
|
items={cateItems}
|
22
23
|
onClick={({ key }) => {
|
23
|
-
|
24
|
+
setTab(key as ChatSettingsTabs);
|
24
25
|
}}
|
25
26
|
selectable
|
26
27
|
selectedKeys={[tab as any]}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Drawer } from 'antd';
|
4
|
+
import { useResponsive, useTheme } from 'antd-style';
|
5
|
+
import isEqual from 'fast-deep-equal';
|
6
|
+
import { memo, useRef, useState } from 'react';
|
7
|
+
import { useTranslation } from 'react-i18next';
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
9
|
+
|
10
|
+
import Header from '@/app/[variants]/(main)/settings/_layout/Desktop/Header';
|
11
|
+
import AgentChat from '@/features/AgentSetting/AgentChat';
|
12
|
+
import AgentMeta from '@/features/AgentSetting/AgentMeta';
|
13
|
+
import AgentModal from '@/features/AgentSetting/AgentModal';
|
14
|
+
import AgentPlugin from '@/features/AgentSetting/AgentPlugin';
|
15
|
+
import AgentPrompt from '@/features/AgentSetting/AgentPrompt';
|
16
|
+
import AgentTTS from '@/features/AgentSetting/AgentTTS';
|
17
|
+
import StoreUpdater from '@/features/AgentSetting/StoreUpdater';
|
18
|
+
import { Provider, createStore } from '@/features/AgentSetting/store';
|
19
|
+
import Footer from '@/features/Setting/Footer';
|
20
|
+
import { useAgentStore } from '@/store/agent';
|
21
|
+
import { agentSelectors } from '@/store/agent/slices/chat';
|
22
|
+
import { ChatSettingsTabs } from '@/store/global/initialState';
|
23
|
+
import { useSessionStore } from '@/store/session';
|
24
|
+
import { sessionMetaSelectors } from '@/store/session/selectors';
|
25
|
+
|
26
|
+
import CategoryContent from './CategoryContent';
|
27
|
+
|
28
|
+
const AgentSettings = memo(() => {
|
29
|
+
const { t } = useTranslation('setting');
|
30
|
+
const id = useSessionStore((s) => s.activeId);
|
31
|
+
const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
|
32
|
+
const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
|
33
|
+
const [showAgentSetting, updateAgentConfig] = useAgentStore((s) => [
|
34
|
+
s.showAgentSetting,
|
35
|
+
s.updateAgentConfig,
|
36
|
+
]);
|
37
|
+
const [updateAgentMeta] = useSessionStore((s) => [
|
38
|
+
s.updateSessionMeta,
|
39
|
+
sessionMetaSelectors.currentAgentTitle(s),
|
40
|
+
]);
|
41
|
+
|
42
|
+
const [tab, setTab] = useState(ChatSettingsTabs.Meta);
|
43
|
+
|
44
|
+
const ref = useRef<any>(null);
|
45
|
+
const theme = useTheme();
|
46
|
+
const { md = true, mobile = false } = useResponsive();
|
47
|
+
|
48
|
+
const category = <CategoryContent setTab={setTab} tab={tab} />;
|
49
|
+
return (
|
50
|
+
<Provider createStore={createStore}>
|
51
|
+
<StoreUpdater
|
52
|
+
config={config}
|
53
|
+
id={id}
|
54
|
+
meta={meta}
|
55
|
+
onConfigChange={updateAgentConfig}
|
56
|
+
onMetaChange={updateAgentMeta}
|
57
|
+
/>
|
58
|
+
<Drawer
|
59
|
+
height={'100vh'}
|
60
|
+
onClose={() => {
|
61
|
+
useAgentStore.setState({ showAgentSetting: false });
|
62
|
+
}}
|
63
|
+
open={showAgentSetting}
|
64
|
+
placement={'bottom'}
|
65
|
+
styles={{
|
66
|
+
body: { padding: 0 },
|
67
|
+
content: {
|
68
|
+
background: theme.colorBgContainer,
|
69
|
+
},
|
70
|
+
}}
|
71
|
+
title={t('header.session')}
|
72
|
+
>
|
73
|
+
<Flexbox height={'100%'} horizontal={md} ref={ref} width={'100%'}>
|
74
|
+
{md ? (
|
75
|
+
<Flexbox padding={16}>{category}</Flexbox>
|
76
|
+
) : (
|
77
|
+
<Header
|
78
|
+
getContainer={() => ref.current}
|
79
|
+
title={t(`agentTab.${tab as ChatSettingsTabs}`)}
|
80
|
+
>
|
81
|
+
{category}
|
82
|
+
</Header>
|
83
|
+
)}
|
84
|
+
<Flexbox
|
85
|
+
align={'center'}
|
86
|
+
gap={mobile ? 0 : 64}
|
87
|
+
paddingInline={mobile ? 0 : 56}
|
88
|
+
style={{
|
89
|
+
background: mobile
|
90
|
+
? theme.colorBgContainer
|
91
|
+
: theme.isDarkMode
|
92
|
+
? theme.colorFillQuaternary
|
93
|
+
: theme.colorBgElevated,
|
94
|
+
minHeight: '100%',
|
95
|
+
overflowX: 'hidden',
|
96
|
+
overflowY: 'auto',
|
97
|
+
paddingTop: mobile ? 0 : 16,
|
98
|
+
}}
|
99
|
+
width={'100%'}
|
100
|
+
>
|
101
|
+
{tab === ChatSettingsTabs.Meta && <AgentMeta />}
|
102
|
+
{tab === ChatSettingsTabs.Prompt && <AgentPrompt modal />}
|
103
|
+
{tab === ChatSettingsTabs.Chat && <AgentChat />}
|
104
|
+
{tab === ChatSettingsTabs.Modal && <AgentModal />}
|
105
|
+
{tab === ChatSettingsTabs.TTS && <AgentTTS />}
|
106
|
+
{tab === ChatSettingsTabs.Plugin && <AgentPlugin />} <Footer />
|
107
|
+
</Flexbox>
|
108
|
+
</Flexbox>
|
109
|
+
</Drawer>
|
110
|
+
</Provider>
|
111
|
+
);
|
112
|
+
});
|
113
|
+
|
114
|
+
export default AgentSettings;
|
@@ -2,23 +2,31 @@
|
|
2
2
|
|
3
3
|
import { ActionIcon } from '@lobehub/ui';
|
4
4
|
import { AlignJustify } from 'lucide-react';
|
5
|
+
import dynamic from 'next/dynamic';
|
5
6
|
import { memo } from 'react';
|
6
7
|
import { useTranslation } from 'react-i18next';
|
7
8
|
|
8
9
|
import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
9
10
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
10
11
|
|
12
|
+
const AgentSettings = dynamic(() => import('./AgentSettings'), {
|
13
|
+
ssr: false,
|
14
|
+
});
|
15
|
+
|
11
16
|
const SettingButton = memo<{ mobile?: boolean }>(({ mobile }) => {
|
12
17
|
const { t } = useTranslation('common');
|
13
18
|
const openChatSettings = useOpenChatSettings();
|
14
19
|
|
15
20
|
return (
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
<>
|
22
|
+
<ActionIcon
|
23
|
+
icon={AlignJustify}
|
24
|
+
onClick={() => openChatSettings()}
|
25
|
+
size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}
|
26
|
+
title={t('header.session', { ns: 'setting' })}
|
27
|
+
/>
|
28
|
+
<AgentSettings />
|
29
|
+
</>
|
22
30
|
);
|
23
31
|
});
|
24
32
|
|
@@ -1,11 +1,10 @@
|
|
1
|
-
import { renderHook } from '@testing-library/react';
|
2
|
-
import urlJoin from 'url-join';
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
3
2
|
import { describe, expect, it, vi } from 'vitest';
|
4
3
|
|
5
4
|
import { INBOX_SESSION_ID } from '@/const/session';
|
6
5
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
7
|
-
import {
|
8
|
-
import { ChatSettingsTabs
|
6
|
+
import { useAgentStore } from '@/store/agent';
|
7
|
+
import { ChatSettingsTabs } from '@/store/global/initialState';
|
9
8
|
import { useSessionStore } from '@/store/session';
|
10
9
|
|
11
10
|
import { useOpenChatSettings } from './useInterceptingRoutes';
|
@@ -50,7 +49,13 @@ describe('useOpenChatSettings', () => {
|
|
50
49
|
it('should handle desktop route for chat settings with session and tab', () => {
|
51
50
|
vi.mocked(useSessionStore).mockReturnValue('456');
|
52
51
|
vi.mocked(useIsMobile).mockReturnValue(false);
|
52
|
+
|
53
53
|
const { result } = renderHook(() => useOpenChatSettings(ChatSettingsTabs.Meta));
|
54
|
-
|
54
|
+
|
55
|
+
act(() => {
|
56
|
+
result.current();
|
57
|
+
});
|
58
|
+
|
59
|
+
expect(useAgentStore.getState().showAgentSetting).toBeTruthy();
|
55
60
|
});
|
56
61
|
});
|
@@ -4,23 +4,25 @@ import urlJoin from 'url-join';
|
|
4
4
|
import { INBOX_SESSION_ID } from '@/const/session';
|
5
5
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
6
6
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
7
|
+
import { useAgentStore } from '@/store/agent';
|
7
8
|
import { ChatSettingsTabs, SettingsTabs } from '@/store/global/initialState';
|
8
9
|
import { useSessionStore } from '@/store/session';
|
9
10
|
|
10
11
|
export const useOpenChatSettings = (tab: ChatSettingsTabs = ChatSettingsTabs.Meta) => {
|
11
12
|
const activeId = useSessionStore((s) => s.activeId);
|
13
|
+
|
14
|
+
const isMobile = useIsMobile();
|
12
15
|
const router = useQueryRoute();
|
13
|
-
const mobile = useIsMobile();
|
14
16
|
|
15
17
|
return useMemo(() => {
|
16
18
|
if (activeId === INBOX_SESSION_ID) {
|
17
19
|
return () => router.push(urlJoin('/settings', SettingsTabs.Agent));
|
18
20
|
}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}
|
25
|
-
}, [
|
21
|
+
|
22
|
+
if (isMobile) return () => router.push('/chat/settings');
|
23
|
+
|
24
|
+
return () => {
|
25
|
+
useAgentStore.setState({ showAgentSetting: true });
|
26
|
+
};
|
27
|
+
}, [activeId, router, tab, isMobile]);
|
26
28
|
};
|
@@ -11,6 +11,7 @@ export interface AgentState {
|
|
11
11
|
agentSettingInstance?: AgentSettingsInstance | null;
|
12
12
|
defaultAgentConfig: LobeAgentConfig;
|
13
13
|
isInboxAgentConfigInit: boolean;
|
14
|
+
showAgentSetting: boolean;
|
14
15
|
updateAgentChatConfigSignal?: AbortController;
|
15
16
|
updateAgentConfigSignal?: AbortController;
|
16
17
|
}
|
@@ -20,4 +21,5 @@ export const initialAgentChatState: AgentState = {
|
|
20
21
|
agentMap: {},
|
21
22
|
defaultAgentConfig: DEFAULT_AGENT_CONFIG,
|
22
23
|
isInboxAgentConfigInit: false,
|
24
|
+
showAgentSetting: false,
|
23
25
|
};
|
@@ -1,23 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import { useLayoutEffect } from 'react';
|
4
|
-
|
5
|
-
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
6
|
-
|
7
|
-
/**
|
8
|
-
* @description: Chat Settings Modal (intercepting routes fallback when hard refresh)
|
9
|
-
* @example: /chat/settings/modal?tab=prompt => /chat/settings
|
10
|
-
* @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
|
11
|
-
*/
|
12
|
-
|
13
|
-
const ChatSettingsModalFallback = () => {
|
14
|
-
const router = useQueryRoute();
|
15
|
-
|
16
|
-
useLayoutEffect(() => {
|
17
|
-
router.replace('/chat/settings', { query: { tab: '' } });
|
18
|
-
}, []);
|
19
|
-
|
20
|
-
return null;
|
21
|
-
};
|
22
|
-
|
23
|
-
export default ChatSettingsModalFallback;
|
@@ -1,61 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import { Skeleton } from 'antd';
|
4
|
-
import isEqual from 'fast-deep-equal';
|
5
|
-
import dynamic from 'next/dynamic';
|
6
|
-
import { PropsWithChildren, memo } from 'react';
|
7
|
-
import { useTranslation } from 'react-i18next';
|
8
|
-
|
9
|
-
import ModalLayout from '@/app/[variants]/@modal/_layout/ModalLayout';
|
10
|
-
import StoreUpdater from '@/features/AgentSetting/StoreUpdater';
|
11
|
-
import { Provider, createStore } from '@/features/AgentSetting/store';
|
12
|
-
import { useChatSettingsTab } from '@/hooks/useChatSettingsTab';
|
13
|
-
import { useAgentStore } from '@/store/agent';
|
14
|
-
import { agentSelectors } from '@/store/agent/slices/chat';
|
15
|
-
import { ChatSettingsTabs } from '@/store/global/initialState';
|
16
|
-
import { useSessionStore } from '@/store/session';
|
17
|
-
import { sessionMetaSelectors } from '@/store/session/selectors';
|
18
|
-
|
19
|
-
import SettingModalLayout from '../../../_layout/SettingModalLayout';
|
20
|
-
|
21
|
-
const CategoryContent = dynamic(() => import('./features/CategoryContent'), {
|
22
|
-
loading: () => <Skeleton paragraph={{ rows: 6 }} title={false} />,
|
23
|
-
ssr: false,
|
24
|
-
});
|
25
|
-
|
26
|
-
const Layout = memo<PropsWithChildren>(({ children }) => {
|
27
|
-
const tab = useChatSettingsTab();
|
28
|
-
const { t } = useTranslation('setting');
|
29
|
-
const id = useSessionStore((s) => s.activeId);
|
30
|
-
const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
|
31
|
-
const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
|
32
|
-
const [updateAgentConfig] = useAgentStore((s) => [s.updateAgentConfig]);
|
33
|
-
const [updateAgentMeta] = useSessionStore((s) => [
|
34
|
-
s.updateSessionMeta,
|
35
|
-
sessionMetaSelectors.currentAgentTitle(s),
|
36
|
-
]);
|
37
|
-
|
38
|
-
return (
|
39
|
-
<ModalLayout>
|
40
|
-
<SettingModalLayout
|
41
|
-
activeTitle={t(`agentTab.${tab as ChatSettingsTabs}`)}
|
42
|
-
category={<CategoryContent />}
|
43
|
-
desc={t('header.sessionDesc')}
|
44
|
-
title={t('header.session')}
|
45
|
-
>
|
46
|
-
<Provider createStore={createStore}>
|
47
|
-
<StoreUpdater
|
48
|
-
config={config}
|
49
|
-
id={id}
|
50
|
-
meta={meta}
|
51
|
-
onConfigChange={updateAgentConfig}
|
52
|
-
onMetaChange={updateAgentMeta}
|
53
|
-
/>
|
54
|
-
{children}
|
55
|
-
</Provider>
|
56
|
-
</SettingModalLayout>
|
57
|
-
</ModalLayout>
|
58
|
-
);
|
59
|
-
});
|
60
|
-
|
61
|
-
export default Layout;
|
@@ -1,56 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import dynamic from 'next/dynamic';
|
4
|
-
|
5
|
-
import { useChatSettingsTab } from '@/hooks/useChatSettingsTab';
|
6
|
-
import { ChatSettingsTabs } from '@/store/global/initialState';
|
7
|
-
|
8
|
-
import Skeleton from './loading';
|
9
|
-
|
10
|
-
const loading = () => <Skeleton />;
|
11
|
-
|
12
|
-
const AgentMeta = dynamic(() => import('@/features/AgentSetting/AgentMeta'), {
|
13
|
-
loading,
|
14
|
-
ssr: false,
|
15
|
-
});
|
16
|
-
const AgentChat = dynamic(() => import('@/features/AgentSetting/AgentChat'), {
|
17
|
-
loading,
|
18
|
-
ssr: false,
|
19
|
-
});
|
20
|
-
const AgentPrompt = dynamic(() => import('@/features/AgentSetting/AgentPrompt'), {
|
21
|
-
loading,
|
22
|
-
ssr: false,
|
23
|
-
});
|
24
|
-
const AgentPlugin = dynamic(() => import('@/features/AgentSetting/AgentPlugin'), {
|
25
|
-
loading,
|
26
|
-
ssr: false,
|
27
|
-
});
|
28
|
-
const AgentModal = dynamic(() => import('@/features/AgentSetting/AgentModal'), {
|
29
|
-
loading,
|
30
|
-
ssr: false,
|
31
|
-
});
|
32
|
-
const AgentTTS = dynamic(() => import('@/features/AgentSetting/AgentTTS'), { loading, ssr: false });
|
33
|
-
|
34
|
-
/**
|
35
|
-
* @description: Agent Settings Modal (intercepting route: /chat/settings/modal )
|
36
|
-
* @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
|
37
|
-
*/
|
38
|
-
|
39
|
-
const Page = () => {
|
40
|
-
const tab = useChatSettingsTab();
|
41
|
-
|
42
|
-
return (
|
43
|
-
<>
|
44
|
-
{tab === ChatSettingsTabs.Meta && <AgentMeta />}
|
45
|
-
{tab === ChatSettingsTabs.Prompt && <AgentPrompt modal />}
|
46
|
-
{tab === ChatSettingsTabs.Chat && <AgentChat />}
|
47
|
-
{tab === ChatSettingsTabs.Modal && <AgentModal />}
|
48
|
-
{tab === ChatSettingsTabs.TTS && <AgentTTS />}
|
49
|
-
{tab === ChatSettingsTabs.Plugin && <AgentPlugin />}
|
50
|
-
</>
|
51
|
-
);
|
52
|
-
};
|
53
|
-
|
54
|
-
Page.displayName = 'AgentSettingModal';
|
55
|
-
|
56
|
-
export default Page;
|
@@ -1,12 +0,0 @@
|
|
1
|
-
import { useQueryState } from 'nuqs';
|
2
|
-
|
3
|
-
import { ChatSettingsTabs } from '@/store/global/initialState';
|
4
|
-
|
5
|
-
export const useChatSettingsTab = () => {
|
6
|
-
const [type] = useQueryState('tab', {
|
7
|
-
clearOnDefault: true,
|
8
|
-
defaultValue: ChatSettingsTabs.Meta,
|
9
|
-
});
|
10
|
-
|
11
|
-
return type as ChatSettingsTabs;
|
12
|
-
};
|