💄 style(settings): clean up settings page copy and entries (#15117)

This commit is contained in:
AmAzing-
2026-05-23 10:04:08 +08:00
committed by GitHub
parent 1c24b9e677
commit 36cc836f2b
15 changed files with 508 additions and 46 deletions
+1 -1
View File
@@ -113,7 +113,7 @@
"modeSelection.title3": "Tell me, so I can tailor it just for you~",
"next": "Next",
"proSettings.connectors.title": "Connect Your Favorite Tools",
"proSettings.devMode.title": "Developer Mode",
"proSettings.devMode.title": "Advanced tools",
"proSettings.model.fixed": "Default model is preset to {{provider}}/{{model}} in this environment.",
"proSettings.model.title": "Default Model Used by the Agent",
"proSettings.title": "Configure Advanced Options in Advance",
+5 -3
View File
@@ -577,8 +577,8 @@
"settingChatAppearance.transitionMode.options.none.value": "None",
"settingChatAppearance.transitionMode.options.smooth": "Smooth",
"settingChatAppearance.transitionMode.title": "Transition Animation",
"settingCommon.devMode.desc": "Enable to show developer-related features and options",
"settingCommon.devMode.title": "Developer Mode",
"settingCommon.devMode.desc": "Show technical details and manual controls for chats, models, and local tools. This does not change model responses.",
"settingCommon.devMode.title": "Advanced tools",
"settingCommon.lang.autoMode": "Follow System",
"settingCommon.lang.title": "Language",
"settingCommon.liteMode.desc": "Simplify the interface and hide advanced features",
@@ -869,6 +869,8 @@
"tab.addCustomMcp.desc": "Manually configure a custom MCP server",
"tab.addCustomSkill": "Add",
"tab.advanced": "Advanced",
"tab.advanced.appUpdates.title": "App updates",
"tab.advanced.toolsAndDiagnostics.title": "Tools and diagnostics",
"tab.advanced.updateChannel.canary": "Canary",
"tab.advanced.updateChannel.canaryDesc": "Triggered on every PR merge, multiple builds per day. Most unstable.",
"tab.advanced.updateChannel.desc": "By default, get notifications for stable updates. The Canary channel receives pre-release builds that may be unstable for production work.",
@@ -876,7 +878,7 @@
"tab.advanced.updateChannel.nightlyDesc": "Automated daily builds with the latest changes.",
"tab.advanced.updateChannel.stable": "Stable",
"tab.advanced.updateChannel.stableDesc": "Production-ready releases.",
"tab.advanced.updateChannel.title": "Update Channel",
"tab.advanced.updateChannel.title": "Update channel",
"tab.agent": "Agent",
"tab.all": "All",
"tab.apikey": "API Keys",
+1 -1
View File
@@ -113,7 +113,7 @@
"modeSelection.title3": "告诉我,为你定制",
"next": "下一步",
"proSettings.connectors.title": "连接你常用的工具",
"proSettings.devMode.title": "开发者模式",
"proSettings.devMode.title": "高级工具",
"proSettings.model.fixed": "默认模型在此环境中预设为 {{provider}}/{{model}}。",
"proSettings.model.title": "助理默认模型",
"proSettings.title": "先配置一些进阶选项",
+4 -2
View File
@@ -577,8 +577,8 @@
"settingChatAppearance.transitionMode.options.none.value": "无",
"settingChatAppearance.transitionMode.options.smooth": "平滑",
"settingChatAppearance.transitionMode.title": "过渡动画",
"settingCommon.devMode.desc": "开启后将显示开发者相关的功能和选项",
"settingCommon.devMode.title": "开发者模式",
"settingCommon.devMode.desc": "显示对话、模型和本地工具的技术细节与手动控制项。不会改变模型回复方式。",
"settingCommon.devMode.title": "高级工具",
"settingCommon.lang.autoMode": "跟随系统",
"settingCommon.lang.title": "语言",
"settingCommon.liteMode.desc": "简化界面并隐藏高级功能",
@@ -869,6 +869,8 @@
"tab.addCustomMcp.desc": "手动配置自定义 MCP 服务器",
"tab.addCustomSkill": "添加",
"tab.advanced": "高级设置",
"tab.advanced.appUpdates.title": "应用更新",
"tab.advanced.toolsAndDiagnostics.title": "工具与诊断",
"tab.advanced.updateChannel.canary": "Canary",
"tab.advanced.updateChannel.canaryDesc": "每次 PR 合并触发构建,一天可能多次。最不稳定。",
"tab.advanced.updateChannel.desc": "默认情况下,接收稳定更新的通知。Canary 渠道会接收可能不适合生产环境的预发布版本。",
+1 -1
View File
@@ -121,7 +121,7 @@ export default {
'modeSelection.title3': 'Tell me, so I can tailor it just for you~',
'next': 'Next',
'proSettings.connectors.title': 'Connect Your Favorite Tools',
'proSettings.devMode.title': 'Developer Mode',
'proSettings.devMode.title': 'Advanced tools',
'proSettings.model.fixed':
'Default model is preset to {{provider}}/{{model}} in this environment.',
'proSettings.model.title': 'Default Model Used by the Agent',
+6 -3
View File
@@ -655,8 +655,9 @@ export default {
'settingChatAppearance.transitionMode.options.none.value': 'None',
'settingChatAppearance.transitionMode.options.smooth': 'Smooth',
'settingChatAppearance.transitionMode.title': 'Transition Animation',
'settingCommon.devMode.desc': 'Enable to show developer-related features and options',
'settingCommon.devMode.title': 'Developer Mode',
'settingCommon.devMode.desc':
'Show technical details and manual controls for chats, models, and local tools. This does not change model responses.',
'settingCommon.devMode.title': 'Advanced tools',
'settingCommon.lang.autoMode': 'Follow System',
'settingCommon.lang.title': 'Language',
'settingCommon.liteMode.desc': 'Simplify the interface and hide advanced features',
@@ -1006,6 +1007,8 @@ When I am ___, I need ___
'systemAgent.translation.title': 'Message Translation',
'tab.about': 'About',
'tab.advanced': 'Advanced',
'tab.advanced.appUpdates.title': 'App updates',
'tab.advanced.toolsAndDiagnostics.title': 'Tools and diagnostics',
'tab.addAgentSkill': 'Add Agent Skill',
'tab.advanced.updateChannel.canary': 'Canary',
'tab.advanced.updateChannel.canaryDesc':
@@ -1016,7 +1019,7 @@ When I am ___, I need ___
'tab.advanced.updateChannel.nightlyDesc': 'Automated daily builds with the latest changes.',
'tab.advanced.updateChannel.stable': 'Stable',
'tab.advanced.updateChannel.stableDesc': 'Production-ready releases.',
'tab.advanced.updateChannel.title': 'Update Channel',
'tab.advanced.updateChannel.title': 'Update channel',
'tab.addCustomMcp': 'Add Custom MCP Skill',
'tab.addCustomMcp.desc': 'Manually configure a custom MCP server',
'tab.addCustomSkill': 'Add',
@@ -0,0 +1,108 @@
import { render, screen } from '@testing-library/react';
import { type ReactNode } from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { initServerConfigStore, Provider } from '@/store/serverConfig/store';
import { useUserStore } from '@/store/user';
import Page from './index';
vi.hoisted(() => {
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: {
getItem: vi.fn(() => null),
removeItem: vi.fn(),
setItem: vi.fn(),
},
});
});
vi.mock('@lobechat/const', async (importOriginal) => ({
...(await importOriginal()),
isDesktop: true,
}));
vi.mock('antd-style', () => ({
createStaticStyles: () => ({
labItem: 'lab-item',
}),
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
vi.mock('@lobehub/ui', () => ({
Form: ({
items,
}: {
items: {
children: { children?: ReactNode; desc?: string; label: string }[];
title: string;
}[];
}) => (
<div>
{items.map((group) => (
<section key={group.title}>
<h2>{group.title}</h2>
{group.children.map((item) => (
<div key={item.label}>
<span>{item.label}</span>
{item.children}
</div>
))}
</section>
))}
</div>
),
Icon: () => null,
Skeleton: () => <div>loading</div>,
}));
vi.mock('@lobehub/ui/base-ui', () => ({
Select: () => <button />,
Switch: () => <button />,
}));
vi.mock('@/routes/(main)/settings/features/SettingHeader', () => ({
default: ({ title }: { title: string }) => <h1>{title}</h1>,
}));
vi.mock('@/services/electron/autoUpdate', () => ({
autoUpdateService: {
getUpdateChannel: vi.fn().mockResolvedValue('stable'),
setUpdateChannel: vi.fn(),
},
}));
const createWrapper = () => {
const Wrapper = ({ children }: { children: ReactNode }) => (
<Provider createStore={() => initServerConfigStore({})}>{children}</Provider>
);
return Wrapper;
};
const initialUserStoreState = useUserStore.getState();
afterEach(() => {
useUserStore.setState(initialUserStoreState, true);
});
describe('Advanced settings page', () => {
it('uses distinct group titles for tools and app updates', () => {
useUserStore.setState({
isUserStateInit: true,
setSettings: vi.fn(),
updateLab: vi.fn(),
});
render(<Page />, { wrapper: createWrapper() });
expect(screen.getByText('tab.advanced.toolsAndDiagnostics.title')).toBeDefined();
expect(screen.getByText('tab.advanced.appUpdates.title')).toBeDefined();
});
});
@@ -75,7 +75,7 @@ const Page = memo(() => {
},
],
extra: loading && <Icon spin icon={Loader2Icon} size={16} style={{ opacity: 0.5 }} />,
title: t('tab.advanced'),
title: t('tab.advanced.toolsAndDiagnostics.title'),
};
const channelOptions = [
@@ -93,7 +93,7 @@ const Page = memo(() => {
label: t('tab.advanced.updateChannel.title'),
},
],
title: t('tab.advanced.updateChannel.title'),
title: t('tab.advanced.appUpdates.title'),
};
const labItems: FormItemProps[] = [
@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest';
import onboarding from '@/locales/default/onboarding';
import setting from '@/locales/default/setting';
describe('settings copy', () => {
it('describes Advanced tools without repeating Developer Mode wording', () => {
expect(setting['settingCommon.devMode.title']).toBe('Advanced tools');
expect(setting['settingCommon.devMode.desc']).toBe(
'Show technical details and manual controls for chats, models, and local tools. This does not change model responses.',
);
expect(onboarding['proSettings.devMode.title']).toBe('Advanced tools');
});
it('uses non-repeating Advanced page group titles', () => {
expect(setting['tab.advanced.toolsAndDiagnostics.title']).toBe('Tools and diagnostics');
expect(setting['tab.advanced.appUpdates.title']).toBe('App updates');
expect(setting['tab.advanced.updateChannel.title']).toBe('Update channel');
});
});
@@ -0,0 +1,78 @@
import { renderHook } from '@testing-library/react';
import { type ReactNode } from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mapFeatureFlagsEnvToState } from '@/config/featureFlags';
import { SettingsTabs } from '@/store/global/initialState';
import { initServerConfigStore, Provider } from '@/store/serverConfig/store';
import { useUserStore } from '@/store/user';
import { useCategory } from './useCategory';
vi.hoisted(() => {
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: {
getItem: vi.fn(() => null),
removeItem: vi.fn(),
setItem: vi.fn(),
},
});
});
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
const createWrapper = (showProvider: boolean) => {
const Wrapper = ({ children }: { children: ReactNode }) => (
<Provider
createStore={() =>
initServerConfigStore({
featureFlags: {
...mapFeatureFlagsEnvToState({
provider_settings: true,
}),
showProvider,
},
})
}
>
{children}
</Provider>
);
return Wrapper;
};
const getItemKeys = () => {
const { result } = renderHook(() => useCategory(), {
wrapper: createWrapper(true),
});
return result.current.flatMap((group) => group.items.map((item) => item.key));
};
const initialUserStoreState = useUserStore.getState();
afterEach(() => {
useUserStore.setState(initialUserStoreState, true);
});
describe('settings useCategory', () => {
it('keeps Provider visible when provider settings are enabled', () => {
expect(getItemKeys()).toContain(SettingsTabs.Provider);
});
it('hides Provider when provider settings are disabled', () => {
const { result } = renderHook(() => useCategory(), {
wrapper: createWrapper(false),
});
const keys = result.current.flatMap((group) => group.items.map((item) => item.key));
expect(keys).not.toContain(SettingsTabs.Provider);
});
});
@@ -61,7 +61,7 @@ export const useCategory = () => {
const { t: tAuth } = useTranslation('auth');
const { t: tSubscription } = useTranslation('subscription');
const mobile = useServerConfigStore((s) => s.isMobile);
const { hideDocs, showApiKeyManage } = useServerConfigStore(featureFlagsSelectors);
const { hideDocs, showApiKeyManage, showProvider } = useServerConfigStore(featureFlagsSelectors);
const [avatar, username] = useUserStore((s) => [
userProfileSelectors.userAvatar(s),
userProfileSelectors.nickName(s),
@@ -134,7 +134,9 @@ export const useCategory = () => {
// Agent group
const agentItems: CategoryItem[] = [
(!enableBusinessFeatures || isDevMode) && {
// Provider settings should not depend on Advanced tools: new users may need
// non-LobeHub providers, and desktop users often bring their own API keys.
showProvider && {
icon: Brain,
key: SettingsTabs.Provider,
label: t('tab.provider'),
@@ -226,6 +228,7 @@ export const useCategory = () => {
hideDocs,
mobile,
showApiKeyManage,
showProvider,
isDevMode,
avatarUrl,
username,
@@ -0,0 +1,143 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { type ReactNode } from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mapFeatureFlagsEnvToState } from '@/config/featureFlags';
import { initServerConfigStore, Provider } from '@/store/serverConfig/store';
import { useUserStore } from '@/store/user';
import AdvancedActions from './Advanced';
vi.hoisted(() => {
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: {
getItem: vi.fn(() => null),
removeItem: vi.fn(),
setItem: vi.fn(),
},
});
});
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
vi.mock('@lobehub/ui', () => ({
Button: ({ children, onClick }: { children: ReactNode; onClick?: () => void }) => (
<button onClick={onClick}>{children}</button>
),
Form: ({
items,
}: {
items: {
children: { children?: ReactNode; desc?: string; label: string }[];
title: string;
}[];
}) => (
<div>
{items.map((group) => (
<section key={group.title}>
<h2>{group.title}</h2>
{group.children.map((item) => (
<div key={item.label}>
<span>{item.label}</span>
{item.desc && <span>{item.desc}</span>}
{item.children}
</div>
))}
</section>
))}
</div>
),
Icon: () => null,
ShikiLobeTheme: {},
}));
vi.mock('antd', () => ({
App: {
useApp: () => ({
message: { success: vi.fn() },
modal: { confirm: vi.fn() },
}),
},
Switch: ({ checked, onChange }: { checked?: boolean; onChange?: (checked: boolean) => void }) => (
<button
aria-checked={checked}
role="switch"
onClick={() => {
onChange?.(!checked);
}}
/>
),
}));
vi.mock('@/business/client/features/AccountDeletion', () => ({
default: () => <div />,
}));
vi.mock('@/features/DataImporter', () => ({
default: ({ children }: { children: ReactNode }) => <>{children}</>,
}));
vi.mock('@/services/config', () => ({
configService: {
exportAll: vi.fn(),
},
}));
const createWrapper = (hideDocs: boolean) => {
const Wrapper = ({ children }: { children: ReactNode }) => (
<Provider
createStore={() =>
initServerConfigStore({
featureFlags: {
...mapFeatureFlagsEnvToState({
commercial_hide_docs: false,
}),
hideDocs,
},
})
}
>
{children}
</Provider>
);
return Wrapper;
};
const initialUserStoreState = useUserStore.getState();
afterEach(() => {
useUserStore.setState(initialUserStoreState, true);
});
describe('AdvancedActions', () => {
it('does not duplicate analytics when About settings are visible', () => {
render(<AdvancedActions />, { wrapper: createWrapper(false) });
expect(screen.queryByText('analytics.title')).toBeNull();
expect(screen.getByText('storage.actions.title')).toBeDefined();
});
it('shows telemetry as a fallback when About settings are hidden', () => {
const updateGeneralConfig = vi.fn();
useUserStore.setState({
settings: { general: { telemetry: true } },
updateGeneralConfig,
});
render(<AdvancedActions />, { wrapper: createWrapper(true) });
expect(screen.getByText('analytics.title')).toBeDefined();
expect(screen.getByText('analytics.telemetry.title')).toBeDefined();
fireEvent.click(screen.getByRole('switch'));
expect(updateGeneralConfig).toHaveBeenCalledWith({ telemetry: false });
});
});
@@ -1,11 +1,9 @@
'use client';
import { BRANDING_NAME } from '@lobechat/business-const';
import { DEFAULT_SETTINGS } from '@lobechat/config';
import { type FormGroupItemType } from '@lobehub/ui';
import { Button, Form, Icon } from '@lobehub/ui';
import { App, Switch } from 'antd';
import isEqual from 'fast-deep-equal';
import { HardDriveDownload, HardDriveUpload } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,17 +15,18 @@ import { configService } from '@/services/config';
import { useChatStore } from '@/store/chat';
import { useFileStore } from '@/store/file';
import { useServerConfigStore } from '@/store/serverConfig';
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
import { featureFlagsSelectors, serverConfigSelectors } from '@/store/serverConfig/selectors';
import { useSessionStore } from '@/store/session';
import { useToolStore } from '@/store/tool';
import { useUserStore } from '@/store/user';
import { settingsSelectors } from '@/store/user/selectors';
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
const AdvancedActions = () => {
const { t } = useTranslation('setting');
const [form] = Form.useForm();
const { message, modal } = App.useApp();
const { hideDocs } = useServerConfigStore(featureFlagsSelectors);
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
const checked = useUserStore(userGeneralSettingsSelectors.telemetry);
const [clearSessions, clearSessionGroups] = useSessionStore((s) => [
s.clearSessions,
s.clearSessionGroups,
@@ -38,8 +37,8 @@ const AdvancedActions = () => {
]);
const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
const removeAllPlugins = useToolStore((s) => s.removeAllPlugins);
const settings = useUserStore(settingsSelectors.currentSettings, isEqual);
const [setSettings, resetSettings] = useUserStore((s) => [s.setSettings, s.resetSettings]);
const resetSettings = useUserStore((s) => s.resetSettings);
const updateGeneralConfig = useUserStore((s) => s.updateGeneralConfig);
const handleClear = useCallback(() => {
modal.confirm({
@@ -59,7 +58,17 @@ const AdvancedActions = () => {
},
title: t('danger.clear.confirm'),
});
}, []);
}, [
clearAllMessages,
clearSessionGroups,
clearSessions,
clearTopics,
message,
modal,
removeAllFiles,
removeAllPlugins,
t,
]);
const handleReset = useCallback(() => {
modal.confirm({
@@ -67,26 +76,11 @@ const AdvancedActions = () => {
okButtonProps: { danger: true },
onOk: () => {
resetSettings();
form.setFieldsValue(DEFAULT_SETTINGS);
message.success(t('danger.reset.success'));
},
title: t('danger.reset.confirm'),
});
}, []);
const analytics: FormGroupItemType = {
children: [
{
children: <Switch />,
desc: t('analytics.telemetry.desc', { appName: BRANDING_NAME }),
label: t('analytics.telemetry.title'),
minWidth: undefined,
name: ['general', 'telemetry'],
valuePropName: 'checked',
},
],
title: t('analytics.title'),
};
}, [message, modal, resetSettings, t]);
const renderExportButtonFormItem = () => {
return {
@@ -146,16 +140,34 @@ const AdvancedActions = () => {
],
title: t('storage.actions.title'),
};
const analytics: FormGroupItemType = {
children: [
{
children: (
<Switch
checked={!!checked}
onChange={(value) => {
updateGeneralConfig({ telemetry: value });
}}
/>
),
desc: t('analytics.telemetry.desc', { appName: BRANDING_NAME }),
label: t('analytics.telemetry.title'),
minWidth: undefined,
valuePropName: 'checked',
},
],
title: t('analytics.title'),
};
return (
<>
<Form
collapsible={false}
form={form}
initialValues={settings}
items={[analytics, system]}
items={hideDocs ? [analytics, system] : [system]}
itemsType={'group'}
variant={'filled'}
onValuesChange={setSettings}
{...FORM_STYLE}
/>
{enableBusinessFeatures && <AccountDeletion />}
@@ -0,0 +1,89 @@
import { renderHook } from '@testing-library/react';
import { type ReactNode } from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mapFeatureFlagsEnvToState } from '@/config/featureFlags';
import { SettingsTabs } from '@/store/global/initialState';
import { initServerConfigStore, Provider } from '@/store/serverConfig/store';
import { useUserStore } from '@/store/user';
import { useCategory } from './useCategory';
vi.hoisted(() => {
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: {
getItem: vi.fn(() => null),
removeItem: vi.fn(),
setItem: vi.fn(),
},
});
});
const navigate = vi.fn();
vi.mock('react-router-dom', () => ({
useNavigate: () => navigate,
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
const createWrapper = (showProvider: boolean) => {
const Wrapper = ({ children }: { children: ReactNode }) => (
<Provider
createStore={() =>
initServerConfigStore({
featureFlags: {
...mapFeatureFlagsEnvToState({
provider_settings: true,
}),
showProvider,
},
})
}
>
{children}
</Provider>
);
return Wrapper;
};
const initialUserStoreState = useUserStore.getState();
afterEach(() => {
navigate.mockReset();
useUserStore.setState(initialUserStoreState, true);
});
describe('mobile settings useCategory', () => {
it('keeps Provider visible and routes to the provider list when provider settings are enabled', () => {
const { result } = renderHook(() => useCategory(), {
wrapper: createWrapper(true),
});
const provider = result.current
.flatMap((group) => group.items)
.find((item) => item.key === SettingsTabs.Provider);
expect(provider).toBeDefined();
provider?.onClick?.();
expect(navigate).toHaveBeenCalledWith('/settings/provider/all');
});
it('hides Provider when provider settings are disabled', () => {
const { result } = renderHook(() => useCategory(), {
wrapper: createWrapper(false),
});
const keys = result.current.flatMap((group) => group.items.map((item) => item.key));
expect(keys).not.toContain(SettingsTabs.Provider);
});
});
@@ -50,7 +50,7 @@ export interface CategoryGroup {
export const useCategory = (): CategoryGroup[] => {
const navigate = useNavigate();
const { t } = useTranslation(['setting', 'auth', 'subscription']);
const { hideDocs, showApiKeyManage } = useServerConfigStore(featureFlagsSelectors);
const { hideDocs, showApiKeyManage, showProvider } = useServerConfigStore(featureFlagsSelectors);
const enableBusinessFeatures = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
const isDevMode = useUserStore((s) => userGeneralSettingsSelectors.config(s).isDevMode);
@@ -100,7 +100,9 @@ export const useCategory = (): CategoryGroup[] => {
: [];
const agent: CategoryItem[] = [
(!enableBusinessFeatures || isDevMode) &&
// Provider settings should not depend on Advanced tools: new users may need
// non-LobeHub providers, and desktop users often bring their own API keys.
showProvider &&
makeItem({ icon: Brain, key: SettingsTabs.Provider, label: t('setting:tab.provider') }),
makeItem({
icon: Sparkles,
@@ -136,5 +138,5 @@ export const useCategory = (): CategoryGroup[] => {
{ items: agent, key: SettingsGroupKey.Agent, title: t('setting:group.aiConfig') },
{ items: system, key: SettingsGroupKey.System, title: t('setting:group.system') },
].filter((group) => group.items.length > 0);
}, [t, enableBusinessFeatures, hideDocs, showApiKeyManage, isDevMode, navigate]);
}, [t, enableBusinessFeatures, hideDocs, showApiKeyManage, showProvider, isDevMode, navigate]);
};